diff --git a/joern-cli/frontends/kotlin2cpg/.scalafmt.conf b/joern-cli/frontends/kotlin2cpg/.scalafmt.conf deleted file mode 100644 index 9ae34c0e00fe..000000000000 --- a/joern-cli/frontends/kotlin2cpg/.scalafmt.conf +++ /dev/null @@ -1,3 +0,0 @@ -version = "2.7.4" -maxColumn = 100 -align = true diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/Kotlin2Cpg.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/Kotlin2Cpg.scala index 99d0cb4701c1..e9eb562a32a0 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/Kotlin2Cpg.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/Kotlin2Cpg.scala @@ -1,38 +1,52 @@ package io.joern.kotlin2cpg import better.files.File - -import java.nio.file.{Files, Paths} -import org.jetbrains.kotlin.psi.KtFile -import org.slf4j.LoggerFactory - -import scala.util.Try -import scala.jdk.CollectionConverters.{CollectionHasAsScala, EnumerationHasAsScala} +import io.joern.kotlin2cpg.compiler.CompilerAPI +import io.joern.kotlin2cpg.compiler.ErrorLoggingMessageCollector import io.joern.kotlin2cpg.files.SourceFilesPicker -import io.joern.kotlin2cpg.passes.{ - AstCreationPass, - ConfigPass, - DependenciesFromMavenCoordinatesPass, - KotlinTypeHintCallLinker, - KotlinTypeRecoveryPassGenerator -} -import io.joern.kotlin2cpg.compiler.{CompilerAPI, ErrorLoggingMessageCollector} -import io.joern.kotlin2cpg.types.{ContentSourcesPicker, DefaultTypeInfoProvider} -import io.joern.kotlin2cpg.utils.PathUtils -import io.joern.x2cpg.X2Cpg.withNewEmptyCpg -import io.joern.x2cpg.{SourceFiles, X2CpgFrontend} -import io.joern.x2cpg.passes.frontend.{MetaDataPass, TypeNodePass, XTypeRecoveryConfig} -import io.joern.x2cpg.utils.dependency.{DependencyResolver, DependencyResolverParams, GradleConfigKeys} import io.joern.kotlin2cpg.interop.JavasrcInterop import io.joern.kotlin2cpg.jar4import.UsesService +import io.joern.kotlin2cpg.passes.* +import io.joern.kotlin2cpg.types.ContentSourcesPicker +import io.joern.kotlin2cpg.types.DefaultTypeInfoProvider +import io.joern.kotlin2cpg.utils.PathUtils +import io.joern.x2cpg.SourceFiles +import io.joern.x2cpg.X2CpgFrontend +import io.joern.x2cpg.X2Cpg.withNewEmptyCpg +import io.joern.x2cpg.passes.frontend.MetaDataPass +import io.joern.x2cpg.passes.frontend.TypeNodePass +import io.joern.x2cpg.utils.dependency.DependencyResolver +import io.joern.x2cpg.utils.dependency.DependencyResolverParams +import io.joern.x2cpg.utils.dependency.GradleConfigKeys import io.joern.x2cpg.SourceFiles.filterFile import io.shiftleft.codepropertygraph.Cpg import io.shiftleft.codepropertygraph.generated.Languages -import io.shiftleft.semanticcpg.language.* import io.shiftleft.utils.IOUtils +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.psi.KtFile +import org.slf4j.LoggerFactory + +import java.nio.file.Files +import java.nio.file.Paths +import scala.jdk.CollectionConverters.CollectionHasAsScala +import scala.jdk.CollectionConverters.EnumerationHasAsScala +import scala.util.Try +import scala.util.matching.Regex object Kotlin2Cpg { - val language = "KOTLIN" + + private val logger = LoggerFactory.getLogger(getClass) + + private val parsingError: String = "KOTLIN2CPG_PARSING_ERROR" + private val jarExtension: String = ".jar" + private val importRegex: Regex = ".*import([^;]*).*".r + + private val defaultKotlinStdlibContentRootJarPaths = Seq( + DefaultContentRootJarPath("jars/kotlin-stdlib-1.9.0.jar", isResource = true), + DefaultContentRootJarPath("jars/kotlin-stdlib-common-1.9.0.jar", isResource = true), + DefaultContentRootJarPath("jars/kotlin-stdlib-jdk8-1.9.0.jar", isResource = true) + ) + case class InputPair(content: String, fileName: String) type InputProvider = () => InputPair @@ -40,167 +54,208 @@ object Kotlin2Cpg { new KotlinTypeRecoveryPassGenerator(cpg).generate().foreach(_.createAndApply()) new KotlinTypeHintCallLinker(cpg).createAndApply() } + } case class KtFileWithMeta(f: KtFile, relativizedPath: String, filename: String) case class FileContentAtPath(content: String, relativizedPath: String, filename: String) class Kotlin2Cpg extends X2CpgFrontend[Config] with UsesService { - protected val logger = LoggerFactory.getLogger(getClass) - val parsingError = "KOTLIN2CPG_PARSING_ERROR" - private val defaultKotlinStdlibContentRootJarPaths = Seq( - DefaultContentRootJarPath("jars/kotlin-stdlib-1.9.0.jar", isResource = true), - DefaultContentRootJarPath("jars/kotlin-stdlib-common-1.9.0.jar", isResource = true), - DefaultContentRootJarPath("jars/kotlin-stdlib-jdk8-1.9.0.jar", isResource = true) - ) - def createCpg(config: Config): Try[Cpg] = { - withNewEmptyCpg(config.outputPath, config) { (cpg, config) => - val sourceDir = config.inputPath - if (!Files.exists(Paths.get(sourceDir))) { - println(s"The specified input path `$sourceDir` is not a file that exists. Exiting.") - System.exit(1) - } - if (!Files.isDirectory(Paths.get(sourceDir))) { - println(s"The specified input path `$sourceDir` is not a directory. Exiting.") - System.exit(1) - } + import Kotlin2Cpg.* + + private def checkSourceDir(sourceDir: String): Unit = { + if (!Files.exists(Paths.get(sourceDir))) { + println(s"The specified input path `$sourceDir` is not a file that exists. Exiting.") + System.exit(1) + } + if (!Files.isDirectory(Paths.get(sourceDir))) { + println(s"The specified input path `$sourceDir` is not a directory. Exiting.") + System.exit(1) + } + } + + private def logMaxHeapSize(): Unit = { + val maxHeapSize = Runtime.getRuntime.maxMemory() + val formattedMaxHeapSize = String.format("%,.2f", maxHeapSize / (1024 * 1024 * 1024).toDouble) + logger.info(s"Max heap size currently set to `${formattedMaxHeapSize}GB`.") + } + + private def gatherFilesWithKtExtension(sourceDir: String, config: Config): List[String] = { + val filesWithKtExtension = SourceFiles.determine( + sourceDir, + Set(".kt"), + ignoredFilesRegex = Option(config.ignoredFilesRegex), + ignoredFilesPath = Option(config.ignoredFiles) + ) + if (filesWithKtExtension.isEmpty) { + println(s"The provided input directory does not contain files ending in '.kt' `$sourceDir`. Exiting.") + System.exit(1) + } + filesWithKtExtension + } - val maxHeapSize = Runtime.getRuntime.maxMemory() - val formattedMaxHeapSize = String.format("%,.2f", maxHeapSize / (1024 * 1024 * 1024).toDouble) - logger.info(s"Max heap size currently set to `${formattedMaxHeapSize}GB`.") + private def gatherFilesWithJavaExtension(sourceDir: String, config: Config): List[String] = { + val filesWithJavaExtension = SourceFiles.determine( + sourceDir, + Set(".java"), + ignoredFilesRegex = Option(config.ignoredFilesRegex), + ignoredFilesPath = Option(config.ignoredFiles) + ) + if (filesWithJavaExtension.nonEmpty) { + logger.info(s"Found ${filesWithJavaExtension.size} files with the `.java` extension.") + } + filesWithJavaExtension + } + + private def gatherDependenciesPaths( + sourceDir: String, + config: Config, + filesWithJavaExtension: List[String] + ): Seq[String] = { + val jar4ImportServiceOpt = config.jar4importServiceUrl.flatMap(reachableServiceMaybe) + if (jar4ImportServiceOpt.isDefined) { + val filesWithKtExtension = gatherFilesWithKtExtension(sourceDir, config) + val importNames = importNamesForFilesAtPaths(filesWithKtExtension ++ filesWithJavaExtension) + logger.trace(s"Found imports: `$importNames`") + dependenciesFromService(jar4ImportServiceOpt.get, importNames) + } else if (config.downloadDependencies) { + downloadDependencies(sourceDir, config) + } else { + logger.info(s"Not downloading any dependencies.") + Seq() + } + } - val jar4ImportServiceOpt = config.jar4importServiceUrl match { - case Some(serviceUrl) => reachableServiceMaybe(serviceUrl) - case None => None + private def gatherMavenCoordinates(sourceDir: String, config: Config): Seq[String] = { + if (config.generateNodesForDependencies) { + logger.info(s"Fetching maven coordinates.") + fetchMavenCoordinates(sourceDir, config) + } else Seq() + } + + private def gatherJarsAtConfigClassPath(sourceDir: String, config: Config): Seq[String] = { + val jarsAtConfigClassPath = findJarsIn(config.classpath) + if (config.classpath.nonEmpty) { + if (jarsAtConfigClassPath.nonEmpty) { + logger.info(s"Found ${jarsAtConfigClassPath.size} jars in the specified classpath.") + } else { + logger.warn("No jars found in the specified classpath.") } + } + jarsAtConfigClassPath + } - val filesWithKtExtension = SourceFiles.determine( - sourceDir, - Set(".kt"), - ignoredFilesRegex = Option(config.ignoredFilesRegex), - ignoredFilesPath = Option(config.ignoredFiles) - ) - if (filesWithKtExtension.isEmpty) { - println(s"The provided input directory does not contain files ending in '.kt' `$sourceDir`. Exiting.") - System.exit(1) + private def gatherDefaultContentRootJars( + sourceDir: String, + config: Config, + filesWithJavaExtension: List[String] + ): Seq[DefaultContentRootJarPath] = { + val stdlibJars = if (config.withStdlibJarsInClassPath) defaultKotlinStdlibContentRootJarPaths else Seq() + val jarsAtConfigClassPath = gatherJarsAtConfigClassPath(sourceDir, config) + val dependenciesPaths = gatherDependenciesPaths(sourceDir, config, filesWithJavaExtension) + val defaultContentRootJars = stdlibJars ++ + jarsAtConfigClassPath.map { path => DefaultContentRootJarPath(path, isResource = false) } ++ + dependenciesPaths.map { path => + DefaultContentRootJarPath(path, isResource = false) } - logger.info(s"Starting CPG generation for input directory `$sourceDir`.") + defaultContentRootJars + } + + private def gatherDirsForSourcesToCompile(sourceDir: String): Seq[String] = { + val dirsForSourcesToCompile = ContentSourcesPicker.dirsForRoot(sourceDir) + if (dirsForSourcesToCompile.isEmpty) { + logger.warn("The list of directories to analyze is empty.") + } + dirsForSourcesToCompile + } - val filesWithJavaExtension = SourceFiles.determine( - sourceDir, - Set(".java"), + private def gatherSourceFiles( + sourceDir: String, + config: Config, + environment: KotlinCoreEnvironment + ): Iterable[KtFileWithMeta] = { + val sourceEntries = entriesForSources(environment.getSourceFiles.asScala, sourceDir) + val sourceFiles = sourceEntries.filter(entry => + SourceFiles.filterFile( + entry.filename, + config.inputPath, ignoredFilesRegex = Option(config.ignoredFilesRegex), ignoredFilesPath = Option(config.ignoredFiles) ) - if (filesWithJavaExtension.nonEmpty) { - logger.info(s"Found ${filesWithJavaExtension.size} files with the `.java` extension.") - } + ) + sourceFiles + } - val dependenciesPaths = if (jar4ImportServiceOpt.isDefined) { - val importNames = importNamesForFilesAtPaths(filesWithKtExtension ++ filesWithJavaExtension) - logger.trace(s"Found imports: `$importNames`") - dependenciesFromService(jar4ImportServiceOpt.get, importNames) - } else if (config.downloadDependencies) { - downloadDependencies(sourceDir, config) - } else { - logger.info(s"Not downloading any dependencies.") - Seq() - } + private def runJavasrcInterop( + cpg: Cpg, + sourceDir: String, + config: Config, + filesWithJavaExtension: List[String], + kotlinAstCreatorTypes: List[String] + ): Unit = { + if (config.includeJavaSourceFiles && filesWithJavaExtension.nonEmpty) { + val javaAstCreator = JavasrcInterop.astCreationPass(config.inputPath, filesWithJavaExtension, cpg) + javaAstCreator.createAndApply() + val javaAstCreatorTypes = javaAstCreator.global.usedTypes.keys().asScala.toList + TypeNodePass + .withRegisteredTypes((javaAstCreatorTypes.toSet -- kotlinAstCreatorTypes.toSet).toList, cpg) + .createAndApply() + } + } - val mavenCoordinates = if (config.generateNodesForDependencies) { - logger.info(s"Fetching maven coordinates.") - fetchMavenCoordinates(sourceDir, config) - } else Seq() - - val jarsAtConfigClassPath = findJarsIn(config.classpath) - if (config.classpath.nonEmpty) { - if (jarsAtConfigClassPath.nonEmpty) { - logger.info(s"Found ${jarsAtConfigClassPath.size} jars in the specified classpath.") - } else { - logger.warn("No jars found in the specified classpath.") - } - } - val stdlibJars = - if (config.withStdlibJarsInClassPath) defaultKotlinStdlibContentRootJarPaths - else Seq() - val defaultContentRootJars = stdlibJars ++ - jarsAtConfigClassPath.map { path => DefaultContentRootJarPath(path, isResource = false) } ++ - dependenciesPaths.map { path => - DefaultContentRootJarPath(path, isResource = false) - } - val messageCollector = new ErrorLoggingMessageCollector - val dirsForSourcesToCompile = ContentSourcesPicker.dirsForRoot(sourceDir) - if (dirsForSourcesToCompile.isEmpty) { - logger.warn("The list of directories to analyze is empty.") - } - val environment = - CompilerAPI.makeEnvironment( - dirsForSourcesToCompile, - filesWithJavaExtension, - defaultContentRootJars, - messageCollector - ) - - val sourceEntries = entriesForSources(environment.getSourceFiles.asScala, sourceDir) - val sources = sourceEntries.filter(entry => - SourceFiles.filterFile( - entry.filename, - config.inputPath, - ignoredFilesRegex = Option(config.ignoredFilesRegex), - ignoredFilesPath = Option(config.ignoredFiles) - ) + def createCpg(config: Config): Try[Cpg] = { + withNewEmptyCpg(config.outputPath, config) { (cpg, config) => + val sourceDir = config.inputPath + logger.info(s"Starting CPG generation for input directory `$sourceDir`.") + + checkSourceDir(sourceDir) + logMaxHeapSize() + + val filesWithJavaExtension = gatherFilesWithJavaExtension(sourceDir, config) + val mavenCoordinates = gatherMavenCoordinates(sourceDir, config) + val defaultContentRootJars = gatherDefaultContentRootJars(sourceDir, config, filesWithJavaExtension) + val dirsForSourcesToCompile = gatherDirsForSourcesToCompile(sourceDir) + val environment = CompilerAPI.makeEnvironment( + dirsForSourcesToCompile, + filesWithJavaExtension, + defaultContentRootJars, + new ErrorLoggingMessageCollector ) - val configFiles = entriesForConfigFiles(SourceFilesPicker.configFiles(sourceDir), sourceDir) - val typeInfoProvider = new DefaultTypeInfoProvider(environment) + + val sourceFiles = gatherSourceFiles(sourceDir, config, environment) + val configFiles = entriesForConfigFiles(SourceFilesPicker.configFiles(sourceDir), sourceDir) new MetaDataPass(cpg, Languages.KOTLIN, config.inputPath).createAndApply() - val astCreator = new AstCreationPass(sources, typeInfoProvider, cpg)(config.schemaValidation) + + val astCreator = + new AstCreationPass(sourceFiles, new DefaultTypeInfoProvider(environment), cpg)(config.schemaValidation) astCreator.createAndApply() + val kotlinAstCreatorTypes = astCreator.global.usedTypes.keys().asScala.toList TypeNodePass.withRegisteredTypes(kotlinAstCreatorTypes, cpg).createAndApply() - if (config.includeJavaSourceFiles && filesWithJavaExtension.nonEmpty) { - val javaAstCreator = JavasrcInterop.astCreationPass(config.inputPath, filesWithJavaExtension, cpg) - javaAstCreator.createAndApply() - val javaAstCreatorTypes = javaAstCreator.global.usedTypes.keys().asScala.toList - TypeNodePass - .withRegisteredTypes((javaAstCreatorTypes.toSet -- kotlinAstCreatorTypes.toSet).toList, cpg) - .createAndApply() - } - - val configCreator = new ConfigPass(configFiles, cpg) - configCreator.createAndApply() - - val dependenciesFromMavenCoordinatesPass = new DependenciesFromMavenCoordinatesPass(mavenCoordinates, cpg) - dependenciesFromMavenCoordinatesPass.createAndApply() - - val hasAtLeastOneMethodNode = cpg.method.take(1).nonEmpty - if (!hasAtLeastOneMethodNode) { - logger.warn("Resulting CPG does not contain any METHOD nodes.") - } + runJavasrcInterop(cpg, sourceDir, config, filesWithJavaExtension, kotlinAstCreatorTypes) + new ConfigPass(configFiles, cpg).createAndApply() + new DependenciesFromMavenCoordinatesPass(mavenCoordinates, cpg).createAndApply() } } private def importNamesForFilesAtPaths(paths: Seq[String]): Seq[String] = { - paths - .flatMap { filePath => - val f = File(filePath) - f.lines.filter(_.startsWith("import")).toSeq - } - .map { line => - val r = ".*import([^;]*).*".r - r.replaceAllIn(line, "$1").trim - } + paths.flatMap(File(_).lines.filter(_.startsWith("import")).toSeq).map(importRegex.replaceAllIn(_, "$1").trim) } - private def downloadDependencies(sourceDir: String, config: Config): scala.collection.Seq[String] = { - val gradleParams = Map( + private def gatherGradleParams(config: Config) = { + Map( GradleConfigKeys.ProjectName -> config.gradleProjectName, GradleConfigKeys.ConfigurationName -> config.gradleConfigurationName ).collect { case (key, Some(value)) => (key, value) } + } + private def downloadDependencies(sourceDir: String, config: Config): Seq[String] = { + val gradleParams = gatherGradleParams(config) val resolverParams = DependencyResolverParams(Map.empty, gradleParams) + DependencyResolver.getDependencies(Paths.get(sourceDir), resolverParams) match { case Some(deps) => logger.info(s"Using ${deps.size} dependency jars.") @@ -213,12 +268,9 @@ class Kotlin2Cpg extends X2CpgFrontend[Config] with UsesService { } private def fetchMavenCoordinates(sourceDir: String, config: Config): Seq[String] = { - val gradleParams = Map( - GradleConfigKeys.ProjectName -> config.gradleProjectName, - GradleConfigKeys.ConfigurationName -> config.gradleConfigurationName - ).collect { case (key, Some(value)) => (key, value) } - + val gradleParams = gatherGradleParams(config) val resolverParams = DependencyResolverParams(Map.empty, gradleParams) + DependencyResolver.getCoordinates(Paths.get(sourceDir), resolverParams) match { case Some(coordinates) => logger.info(s"Found ${coordinates.size} maven coordinates.") @@ -231,7 +283,6 @@ class Kotlin2Cpg extends X2CpgFrontend[Config] with UsesService { } private def findJarsIn(dirs: Set[String]) = { - val jarExtension = ".jar" dirs.foldLeft(Seq[String]())((acc, classpathEntry) => { val f = File(classpathEntry) val files = diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstCreator.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstCreator.scala index 13cfe88495ff..1435c0118414 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstCreator.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstCreator.scala @@ -2,29 +2,39 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants import io.joern.kotlin2cpg.KtFileWithMeta -import io.joern.kotlin2cpg.ast.Nodes.{namespaceBlockNode, operatorCallNode} -import io.joern.kotlin2cpg.types.{TypeConstants, TypeInfoProvider, TypeRenderer} -import io.shiftleft.codepropertygraph.generated.nodes.* -import io.shiftleft.codepropertygraph.generated.* -import io.shiftleft.passes.IntervalKeyPool -import io.joern.x2cpg.{Ast, AstCreatorBase, AstNodeBuilder, Defines, ValidationMode} +import io.joern.kotlin2cpg.ast.Nodes.namespaceBlockNode +import io.joern.kotlin2cpg.ast.Nodes.operatorCallNode +import io.joern.kotlin2cpg.datastructures.Scope +import io.joern.kotlin2cpg.types.TypeConstants +import io.joern.kotlin2cpg.types.TypeInfoProvider +import io.joern.kotlin2cpg.types.TypeRenderer +import io.joern.x2cpg.Ast +import io.joern.x2cpg.AstCreatorBase +import io.joern.x2cpg.AstNodeBuilder +import io.joern.x2cpg.Defines +import io.joern.x2cpg.ValidationMode import io.joern.x2cpg.datastructures.Global import io.joern.x2cpg.datastructures.Stack.* -import io.joern.kotlin2cpg.datastructures.Scope import io.joern.x2cpg.utils.NodeBuilders.newMethodReturnNode +import io.shiftleft.codepropertygraph.generated.* +import io.shiftleft.codepropertygraph.generated.nodes.* +import io.shiftleft.passes.IntervalKeyPool +import io.shiftleft.semanticcpg.language.* import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal import org.jetbrains.kotlin.com.intellij.psi.PsiElement -import org.jetbrains.kotlin.descriptors.{DescriptorVisibilities, DescriptorVisibility} +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.DescriptorVisibility +import org.jetbrains.kotlin.lexer.KtToken +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.lexer.{KtToken, KtTokens} -import org.slf4j.{Logger, LoggerFactory} +import org.slf4j.Logger +import org.slf4j.LoggerFactory import overflowdb.BatchedUpdate.DiffGraphBuilder +import java.io.PrintWriter +import java.io.StringWriter import scala.annotation.tailrec import scala.collection.mutable -import io.shiftleft.semanticcpg.language.* - -import java.io.{PrintWriter, StringWriter} import scala.jdk.CollectionConverters.* case class BindingInfo(node: NewBinding, edgeMeta: Seq[(NewNode, NewNode, String)]) @@ -52,7 +62,7 @@ class AstCreator(fileWithMeta: KtFileWithMeta, xTypeInfoProvider: TypeInfoProvid protected val relativizedPath: String = fileWithMeta.relativizedPath protected val scope: Scope[String, DeclarationNew, NewNode] = new Scope() - protected val debugScope = mutable.Stack.empty[KtDeclaration] + protected val debugScope: mutable.Stack[KtDeclaration] = mutable.Stack.empty[KtDeclaration] def createAst(): DiffGraphBuilder = { implicit val typeInfoProvider: TypeInfoProvider = xTypeInfoProvider @@ -130,7 +140,7 @@ class AstCreator(fileWithMeta: KtFileWithMeta, xTypeInfoProvider: TypeInfoProvid else node.importedEntity.getOrElse("") } - protected def storeInDiffGraph(ast: Ast): Unit = { + private def storeInDiffGraph(ast: Ast): Unit = { Ast.storeInDiffGraph(ast, diffGraph) for { @@ -273,7 +283,7 @@ class AstCreator(fileWithMeta: KtFileWithMeta, xTypeInfoProvider: TypeInfoProvid } } - def astForFile(fileWithMeta: KtFileWithMeta)(implicit typeInfoProvider: TypeInfoProvider): Ast = { + private def astForFile(fileWithMeta: KtFileWithMeta)(implicit typeInfoProvider: TypeInfoProvider): Ast = { val ktFile = fileWithMeta.f val importDirectives = ktFile.getImportList.getImports.asScala @@ -344,7 +354,7 @@ class AstCreator(fileWithMeta: KtFileWithMeta, xTypeInfoProvider: TypeInfoProvid case p: KtProperty => astsForProperty(p) case unhandled => logger.error( - s"Unknown declaration type encountered in this file `${relativizedPath}` with text `${unhandled.getText}` and class `${unhandled.getClass}`!" + s"Unknown declaration type encountered in this file `$relativizedPath` with text `${unhandled.getText}` and class `${unhandled.getClass}`!" ) Seq() } @@ -355,7 +365,7 @@ class AstCreator(fileWithMeta: KtFileWithMeta, xTypeInfoProvider: TypeInfoProvid val printWriter = new PrintWriter(stringWriter) exception.printStackTrace(printWriter) logger.warn( - s"Caught exception while processing decl in this file `${relativizedPath}`:\n$declText\n${stringWriter.toString}" + s"Caught exception while processing decl in this file `$relativizedPath`:\n$declText\n${stringWriter.toString}" ) Seq() } diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForDeclarationsCreator.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForDeclarationsCreator.scala index 44c0aaf72cd8..14c9f1241ec0 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForDeclarationsCreator.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForDeclarationsCreator.scala @@ -4,12 +4,23 @@ import io.joern.kotlin2cpg.Constants import io.joern.kotlin2cpg.ast.Nodes.operatorCallNode import io.joern.kotlin2cpg.psi.PsiUtils import io.joern.kotlin2cpg.psi.PsiUtils.nonUnderscoreDestructuringEntries -import io.joern.kotlin2cpg.types.{AnonymousObjectContext, TypeConstants, TypeInfoProvider} +import io.joern.kotlin2cpg.types.AnonymousObjectContext +import io.joern.kotlin2cpg.types.TypeConstants +import io.joern.kotlin2cpg.types.TypeInfoProvider +import io.joern.x2cpg.Ast +import io.joern.x2cpg.Defines +import io.joern.x2cpg.ValidationMode import io.joern.x2cpg.utils.NodeBuilders -import io.joern.x2cpg.utils.NodeBuilders.{newBindingNode, newIdentifierNode, newMethodReturnNode} -import io.joern.x2cpg.{Ast, AstNodeBuilder, Defines, ValidationMode} -import io.shiftleft.codepropertygraph.generated.nodes.{NewBlock, NewCall, NewMethod, NewTypeDecl} -import io.shiftleft.codepropertygraph.generated.{DispatchTypes, EdgeTypes, Operators} +import io.joern.x2cpg.utils.NodeBuilders.newBindingNode +import io.joern.x2cpg.utils.NodeBuilders.newIdentifierNode +import io.joern.x2cpg.utils.NodeBuilders.newMethodReturnNode +import io.shiftleft.codepropertygraph.generated.DispatchTypes +import io.shiftleft.codepropertygraph.generated.EdgeTypes +import io.shiftleft.codepropertygraph.generated.Operators +import io.shiftleft.codepropertygraph.generated.nodes.NewBlock +import io.shiftleft.codepropertygraph.generated.nodes.NewCall +import io.shiftleft.codepropertygraph.generated.nodes.NewMethod +import io.shiftleft.codepropertygraph.generated.nodes.NewTypeDecl import io.shiftleft.semanticcpg.language.* import org.jetbrains.kotlin.psi.* @@ -150,8 +161,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { classDeclarations.toSeq .collectAll[KtClassOrObject] .filterNot(typeInfoProvider.isCompanionObject) - .map(astsForDeclaration(_)) - .flatten + .flatMap(astsForDeclaration(_)) val classFunctions = Option(ktClass.getBody) .map(_.getFunctions.asScala.collect { case f: KtNamedFunction => f }) @@ -228,7 +238,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { } if (typedInit.isEmpty) { logger.warn( - s"Unhandled case for destructuring declaration: `${expr.getText}`; type: `${expr.getInitializer.getClass}` in this file `${relativizedPath}`." + s"Unhandled case for destructuring declaration: `${expr.getText}`; type: `${expr.getInitializer.getClass}` in this file `$relativizedPath`." ) return Seq() } @@ -266,19 +276,22 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { ) val assignmentNode = operatorCallNode(Operators.assignment, s"$tmpName = ${Constants.alloc}", None) callAst(assignmentNode, List(assignmentLhsAst, Ast(assignmentRhsNode))) - } else if (expr.getInitializer.isInstanceOf[KtArrayAccessExpression]) { - astForArrayAccess(expr.getInitializer.asInstanceOf[KtArrayAccessExpression], None, None) - } else if (expr.getInitializer.isInstanceOf[KtPostfixExpression]) { - astForPostfixExpression(expr.getInitializer.asInstanceOf[KtPostfixExpression], None, None) - } else if (expr.getInitializer.isInstanceOf[KtWhenExpression]) { - astForWhenAsExpression(expr.getInitializer.asInstanceOf[KtWhenExpression], None, None) - } else if (expr.getInitializer.isInstanceOf[KtIfExpression]) { - astForIfAsExpression(expr.getInitializer.asInstanceOf[KtIfExpression], None, None) } else { - val assignmentNode = operatorCallNode(Operators.assignment, s"$tmpName = ${rhsCall.getText}", None) - val assignmentRhsAst = - astsForExpression(rhsCall, None).headOption.getOrElse(Ast(unknownNode(rhsCall, Constants.empty))) - callAst(assignmentNode, List(assignmentLhsAst, assignmentRhsAst)) + expr.getInitializer match { + case accessExpression: KtArrayAccessExpression => + astForArrayAccess(accessExpression, None, None) + case expression: KtPostfixExpression => + astForPostfixExpression(expression, None, None) + case expression: KtWhenExpression => + astForWhenAsExpression(expression, None, None) + case expression: KtIfExpression => + astForIfAsExpression(expression, None, None) + case _ => + val assignmentNode = operatorCallNode(Operators.assignment, s"$tmpName = ${rhsCall.getText}", None) + val assignmentRhsAst = + astsForExpression(rhsCall, None).headOption.getOrElse(Ast(unknownNode(rhsCall, Constants.empty))) + callAst(assignmentNode, List(assignmentLhsAst, assignmentRhsAst)) + } } val tmpAssignmentPrologue = rhsCall match { case call: KtCallExpression if isCtor => @@ -328,7 +341,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { )(implicit typeInfoProvider: TypeInfoProvider): Seq[Ast] = { val typedInit = Option(expr.getInitializer).collect { case e: KtNameReferenceExpression => e } if (typedInit.isEmpty) { - logger.warn(s"Unhandled case for destructuring declaration: `${expr.getText}` in this file `${relativizedPath}`.") + logger.warn(s"Unhandled case for destructuring declaration: `${expr.getText}` in this file `$relativizedPath`.") return Seq() } val destructuringRHS = typedInit.get @@ -624,7 +637,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { } } - def astForMember(decl: KtDeclaration)(implicit typeInfoProvider: TypeInfoProvider): Ast = { + private def astForMember(decl: KtDeclaration)(implicit typeInfoProvider: TypeInfoProvider): Ast = { val name = Option(decl.getName).getOrElse(TypeConstants.any) val explicitTypeName = decl.getOriginalElement match { case p: KtProperty if p.getTypeReference != null => p.getTypeReference.getText diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForExpressionsCreator.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForExpressionsCreator.scala index a1fbb0c68112..f8f1da7843cb 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForExpressionsCreator.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForExpressionsCreator.scala @@ -2,11 +2,17 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants import io.joern.kotlin2cpg.ast.Nodes.operatorCallNode -import io.joern.kotlin2cpg.types.{CallKinds, TypeConstants, TypeInfoProvider} -import io.joern.x2cpg.{Ast, Defines, ValidationMode} +import io.joern.kotlin2cpg.types.CallKinds +import io.joern.kotlin2cpg.types.TypeConstants +import io.joern.kotlin2cpg.types.TypeInfoProvider +import io.joern.x2cpg.Ast +import io.joern.x2cpg.Defines +import io.joern.x2cpg.ValidationMode +import io.shiftleft.codepropertygraph.generated.DispatchTypes +import io.shiftleft.codepropertygraph.generated.Operators import io.shiftleft.codepropertygraph.generated.nodes.NewMethodRef -import io.shiftleft.codepropertygraph.generated.{DispatchTypes, Operators} -import org.jetbrains.kotlin.lexer.{KtToken, KtTokens} +import org.jetbrains.kotlin.lexer.KtToken +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import scala.jdk.CollectionConverters.* @@ -63,7 +69,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { } case _ => logger.warn( - s"Unhandled operator token type `${opRef.getOperationSignTokenType}` for expression `${expr.getText}` in this file `${relativizedPath}`." + s"Unhandled operator token type `${opRef.getOperationSignTokenType}` for expression `${expr.getText}` in this file `$relativizedPath`." ) Some(Constants.unknownOperator) } @@ -501,7 +507,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { (asts.dropRight(1), asts.lastOption.getOrElse(Ast(unknownNode(arg.getArgumentExpression, Constants.empty)))) } val astsForTrails = argAstsWithTrail.map(_._2) - val astsForNonTrails = argAstsWithTrail.map(_._1).flatten + val astsForNonTrails = argAstsWithTrail.flatMap(_._1) val (fullName, signature) = typeInfoProvider.fullNameWithSignature(expr, (TypeConstants.any, TypeConstants.any)) registerType(typeInfoProvider.expressionType(expr, TypeConstants.any)) @@ -580,9 +586,9 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { val typeFullName = registerType(typeInfoProvider.expressionType(expression, TypeConstants.any)) val identifier = identifierNode(arrayExpr, arrayExpr.getText, arrayExpr.getText, typeFullName) val identifierAst = astWithRefEdgeMaybe(arrayExpr.getText, identifier) - val astsForIndexExpr = expression.getIndexExpressions.asScala.zipWithIndex.map { case (expr, idx) => + val astsForIndexExpr = expression.getIndexExpressions.asScala.zipWithIndex.flatMap { case (expr, idx) => astsForExpression(expr, Option(idx + 1)) - }.flatten + } val callNode = operatorCallNode( Operators.indexAccess, diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForFunctionsCreator.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForFunctionsCreator.scala index 6e0039260256..015368771d58 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForFunctionsCreator.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForFunctionsCreator.scala @@ -2,14 +2,20 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants import io.joern.kotlin2cpg.ast.Nodes.modifierNode -import io.joern.kotlin2cpg.types.{TypeConstants, TypeInfoProvider} +import io.joern.kotlin2cpg.types.TypeConstants +import io.joern.kotlin2cpg.types.TypeInfoProvider +import io.joern.x2cpg.Ast +import io.joern.x2cpg.ValidationMode import io.joern.x2cpg.datastructures.Stack.StackWrapper import io.joern.x2cpg.utils.NodeBuilders -import io.joern.x2cpg.utils.NodeBuilders.{newBindingNode, newClosureBindingNode, newMethodReturnNode, newModifierNode} -import io.joern.x2cpg.{Ast, AstNodeBuilder, ValidationMode} +import io.joern.x2cpg.utils.NodeBuilders.newBindingNode +import io.joern.x2cpg.utils.NodeBuilders.newClosureBindingNode +import io.joern.x2cpg.utils.NodeBuilders.newMethodReturnNode +import io.joern.x2cpg.utils.NodeBuilders.newModifierNode +import io.shiftleft.codepropertygraph.generated.EdgeTypes +import io.shiftleft.codepropertygraph.generated.EvaluationStrategies +import io.shiftleft.codepropertygraph.generated.ModifierTypes import io.shiftleft.codepropertygraph.generated.nodes.* -import io.shiftleft.codepropertygraph.generated.{EdgeTypes, EvaluationStrategies, ModifierTypes} -import io.shiftleft.semanticcpg.language.* import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.psi.* diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForPrimitivesCreator.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForPrimitivesCreator.scala index 9150485d23c8..68a92f5cca8b 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForPrimitivesCreator.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForPrimitivesCreator.scala @@ -1,36 +1,36 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants -import io.joern.kotlin2cpg.ast.Nodes.{namespaceBlockNode, operatorCallNode} -import io.joern.kotlin2cpg.types.{TypeConstants, TypeInfoProvider} -import io.joern.x2cpg.{Ast, Defines, ValidationMode} -import io.shiftleft.codepropertygraph.generated.{DispatchTypes, Operators} -import io.shiftleft.codepropertygraph.generated.nodes.{ - NewAnnotation, - NewAnnotationLiteral, - NewImport, - NewLocal, - NewMember, - NewMethodParameterIn -} -import org.jetbrains.kotlin.psi.{ - KtAnnotationEntry, - KtClassLiteralExpression, - KtConstantExpression, - KtImportDirective, - KtNameReferenceExpression, - KtStringTemplateExpression, - KtSuperExpression, - KtThisExpression, - KtTypeAlias, - KtTypeReference -} +import io.joern.kotlin2cpg.ast.Nodes.namespaceBlockNode +import io.joern.kotlin2cpg.ast.Nodes.operatorCallNode +import io.joern.kotlin2cpg.types.TypeConstants +import io.joern.kotlin2cpg.types.TypeInfoProvider +import io.joern.x2cpg.Ast +import io.joern.x2cpg.Defines +import io.joern.x2cpg.ValidationMode +import io.shiftleft.codepropertygraph.generated.DispatchTypes +import io.shiftleft.codepropertygraph.generated.Operators +import io.shiftleft.codepropertygraph.generated.nodes.NewAnnotation +import io.shiftleft.codepropertygraph.generated.nodes.NewAnnotationLiteral +import io.shiftleft.codepropertygraph.generated.nodes.NewImport +import io.shiftleft.codepropertygraph.generated.nodes.NewLocal +import io.shiftleft.codepropertygraph.generated.nodes.NewMember +import io.shiftleft.codepropertygraph.generated.nodes.NewMethodParameterIn import io.shiftleft.semanticcpg.language.* import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal - -import scala.jdk.CollectionConverters.* +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtClassLiteralExpression +import org.jetbrains.kotlin.psi.KtConstantExpression +import org.jetbrains.kotlin.psi.KtImportDirective +import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.psi.KtStringTemplateExpression +import org.jetbrains.kotlin.psi.KtSuperExpression +import org.jetbrains.kotlin.psi.KtThisExpression +import org.jetbrains.kotlin.psi.KtTypeAlias +import org.jetbrains.kotlin.psi.KtTypeReference import scala.annotation.unused +import scala.jdk.CollectionConverters.* import scala.util.Try trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { @@ -45,8 +45,7 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { val typeFullName = registerType(typeInfoProvider.expressionType(expr, TypeConstants.any)) val node = literalNode(expr, expr.getText, typeFullName) val annotationAsts = annotations.map(astForAnnotationEntry) - Ast(withArgumentName(withArgumentIndex(node, argIdx), argName)) - .withChildren(annotationAsts) + Ast(withArgumentName(withArgumentIndex(node, argIdx), argName)).withChildren(annotationAsts) } def astForStringTemplate( @@ -86,12 +85,10 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { argName: Option[String], annotations: Seq[KtAnnotationEntry] = Seq() )(implicit typeInfoProvider: TypeInfoProvider): Ast = { - val isReferencingMember = - scope.lookupVariable(expr.getIdentifier.getText) match { - case Some(_: NewMember) => true - case _ => false - } - + val isReferencingMember = scope.lookupVariable(expr.getIdentifier.getText) match { + case Some(_: NewMember) => true + case _ => false + } val outAst = if (typeInfoProvider.isReferenceToClass(expr)) astForNameReferenceToType(expr, argIdx) else if (isReferencingMember) astForNameReferenceToMember(expr, argIdx) @@ -255,7 +252,7 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { val children = entry.getValueArguments.asScala.flatMap { varg => varg.getArgumentExpression match { - case ste: KtStringTemplateExpression if ste.getEntries.size == 1 => + case ste: KtStringTemplateExpression if ste.getEntries.length == 1 => val node = NewAnnotationLiteral().code(ste.getText) Some(Ast(node)) case ce: KtConstantExpression => diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForStatementsCreator.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForStatementsCreator.scala index 68fd19bd6f1a..e4dbb2acb43a 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForStatementsCreator.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/AstForStatementsCreator.scala @@ -2,35 +2,35 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants import io.joern.kotlin2cpg.ast.Nodes.operatorCallNode -import io.joern.kotlin2cpg.types.{TypeConstants, TypeInfoProvider} -import io.joern.x2cpg.{Ast, AstNodeBuilder, ValidationMode} +import io.joern.kotlin2cpg.types.TypeConstants +import io.joern.kotlin2cpg.types.TypeInfoProvider +import io.joern.x2cpg.Ast +import io.joern.x2cpg.ValidationMode import io.joern.x2cpg.utils.NodeBuilders.newIdentifierNode +import io.shiftleft.codepropertygraph.generated.ControlStructureTypes +import io.shiftleft.codepropertygraph.generated.DispatchTypes +import io.shiftleft.codepropertygraph.generated.Operators import io.shiftleft.codepropertygraph.generated.nodes.NewLocal -import io.shiftleft.codepropertygraph.generated.{ControlStructureTypes, DispatchTypes, Operators} -import org.jetbrains.kotlin.psi.{ - KtAnnotationEntry, - KtBlockExpression, - KtBreakExpression, - KtClassOrObject, - KtContainerNodeForControlStructureBody, - KtContinueExpression, - KtDoWhileExpression, - KtExpression, - KtForExpression, - KtIfExpression, - KtNamedFunction, - KtProperty, - KtPsiUtil, - KtTryExpression, - KtWhenConditionWithExpression, - KtWhenEntry, - KtWhenExpression, - KtWhileExpression -} +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtBreakExpression +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtContainerNodeForControlStructureBody +import org.jetbrains.kotlin.psi.KtContinueExpression +import org.jetbrains.kotlin.psi.KtDoWhileExpression +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.psi.KtIfExpression +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.psi.KtTryExpression +import org.jetbrains.kotlin.psi.KtWhenConditionWithExpression +import org.jetbrains.kotlin.psi.KtWhenEntry +import org.jetbrains.kotlin.psi.KtWhenExpression +import org.jetbrains.kotlin.psi.KtWhileExpression import scala.jdk.CollectionConverters.* -import scala.collection.mutable -import io.shiftleft.semanticcpg.language.* trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { this: AstCreator => @@ -277,7 +277,7 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { else astForIfAsExpression(expr, argIdx, argNameMaybe, annotations) } - def astForIfAsControlStructure(expr: KtIfExpression, annotations: Seq[KtAnnotationEntry] = Seq())(implicit + private def astForIfAsControlStructure(expr: KtIfExpression, annotations: Seq[KtAnnotationEntry] = Seq())(implicit typeInfoProvider: TypeInfoProvider ): Ast = { val conditionAst = astsForExpression(expr.getCondition, None).headOption @@ -338,7 +338,7 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { .withChildren(annotations.map(astForAnnotationEntry)) } - def astForWhenAsStatement(expr: KtWhenExpression, argIdx: Option[Int])(implicit + private def astForWhenAsStatement(expr: KtWhenExpression, argIdx: Option[Int])(implicit typeInfoProvider: TypeInfoProvider ): Ast = { val (astForSubject, finalAstForSubject) = Option(expr.getSubjectExpression) match { @@ -352,7 +352,7 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { } (astForSubject, finalAstForSubject) case _ => - logger.warn(s"Subject Expression empty in this file `${relativizedPath}`.") + logger.warn(s"Subject Expression empty in this file `$relativizedPath`.") (Ast(), Ast()) } @@ -388,7 +388,7 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { val subjectExpressionAsts = Option(expr.getSubjectExpression) match { case Some(subjectExpression) => astsForExpression(subjectExpression, None) case _ => - logger.warn(s"Subject Expression empty in this file `${relativizedPath}`.") + logger.warn(s"Subject Expression empty in this file `$relativizedPath`.") Seq.empty } val subjectBlock = blockNode(expr.getSubjectExpression, "", "") @@ -432,6 +432,11 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { val newElseAst = callAst(callNode, Seq(condAst, entryExprAst, elseAst)) elseAst = newElseAst + case Some(cond) => + logger.debug( + s"Creating empty AST node for unknown condition expression `${cond.getClass}` with text `${cond.getText}`." + ) + Seq(Ast(unknownNode(expr, Option(expr).map(_.getText).getOrElse(Constants.codePropUndefinedValue)))) case None => // This is the 'else' branch of 'when'. // and thus first in reverse order, if exists @@ -497,9 +502,9 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { typeInfoProvider.expressionType(expr.getTryBlock.getStatements.asScala.last, TypeConstants.any) ) val tryBlockAst = astsForExpression(expr.getTryBlock, None).headOption.getOrElse(Ast()) - val clauseAsts = expr.getCatchClauses.asScala.toSeq.map { entry => + val clauseAsts = expr.getCatchClauses.asScala.toSeq.flatMap { entry => astsForExpression(entry.getCatchBody, None) - }.flatten + } val node = operatorCallNode(Operators.tryCatch, expr.getText, Option(typeFullName), line(expr), column(expr)) .argumentName(argNameMaybe) @@ -555,7 +560,7 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { } val declarationAsts = declarations.flatMap(astsForDeclaration) val allStatementsButLast = statements.dropRight(1) - val allStatementsButLastAsts = allStatementsButLast.map(astsForExpression(_, None)).flatten + val allStatementsButLastAsts = allStatementsButLast.flatMap(astsForExpression(_, None)) val lastStatementAstWithTail = if (implicitReturnAroundLastStatement && statements.nonEmpty) { diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/Nodes.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/Nodes.scala index b53cd8453477..edd53027db1a 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/Nodes.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/Nodes.scala @@ -1,7 +1,7 @@ package io.joern.kotlin2cpg.ast -import io.shiftleft.codepropertygraph.generated.{DispatchTypes} -import io.shiftleft.codepropertygraph.generated.nodes.{NewCall, NewJumpTarget, NewModifier, NewNamespaceBlock} +import io.shiftleft.codepropertygraph.generated.DispatchTypes +import io.shiftleft.codepropertygraph.generated.nodes.{NewCall, NewModifier, NewNamespaceBlock} object Nodes { diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/compiler/CompilerAPI.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/compiler/CompilerAPI.scala index d4a98a195e0b..da7db3f2210b 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/compiler/CompilerAPI.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/compiler/CompilerAPI.scala @@ -6,7 +6,7 @@ import java.io.{File, FileOutputStream} import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot import org.jetbrains.kotlin.cli.jvm.compiler.{EnvironmentConfigFiles, KotlinCoreEnvironment} -import org.jetbrains.kotlin.cli.jvm.config.{JavaSourceRoot, JvmClasspathRoot, JvmModulePathRoot} +import org.jetbrains.kotlin.cli.jvm.config.{JavaSourceRoot, JvmClasspathRoot} import org.jetbrains.kotlin.config.{CommonConfigurationKeys, CompilerConfiguration, JVMConfigurationKeys} import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer @@ -98,4 +98,5 @@ class ErrorLoggingMessageCollector extends MessageCollector { } override def hasErrors: Boolean = false override def clear(): Unit = {} + } diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/files/SourceFilesPicker.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/files/SourceFilesPicker.scala index 52be5692ee91..b4adef14a89e 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/files/SourceFilesPicker.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/files/SourceFilesPicker.scala @@ -8,62 +8,51 @@ import org.slf4j.LoggerFactory object SourceFilesPicker { private val logger = LoggerFactory.getLogger(getClass) + private val substringsToFilterFor = List( + ".idea", + "target", + "build", + "integrationTest", + "integrationtest", + "androidTest", + "sharedTest", + "fixtures", + "commonTest", + "jvmTest", + "test" + ) + def shouldFilter(fileName: String): Boolean = { - val substringsToFilterFor = - List( - ".idea", - "target", - "build", - "integrationTest", - "integrationtest", - "androidTest", - "sharedTest", - "fixtures", - "commonTest", - "jvmTest", - "test" - ) - val containsUnwantedSubstring = - substringsToFilterFor.exists { str => - fileName.contains(str) - } - val extensionsToFilterFor = List() - val hasUnwantedExt = { - extensionsToFilterFor.exists { ext => - fileName.endsWith(ext) - } - } + val containsUnwantedSubstring = substringsToFilterFor.exists(fileName.contains) - val isAndroidLayoutXml = - fileName.endsWith("xml") && (fileName.contains("drawable") || fileName.contains("layout")) + val isAndroidLayoutXml = fileName.endsWith("xml") && (fileName.contains("drawable") || fileName.contains("layout")) val containsSrcTest = fileName.contains("src/test") val isSettingsXml = fileName.endsWith("strings.xml") // some projects contain many i18n files val containsBenchmarks = fileName.contains("benchmarks") val isBuildGradle = fileName.endsWith("build.gradle") || fileName.endsWith("build.gradle.kts") (containsUnwantedSubstring && !isBuildGradle) || - hasUnwantedExt || isSettingsXml || containsSrcTest || isAndroidLayoutXml || containsBenchmarks } - protected def isConfigFile(fileName: String): Boolean = { + private def isConfigFile(fileName: String): Boolean = { isXmlFile(fileName) || isGradleFile(fileName) || isKotlinScript(fileName) } - protected def isXmlFile(fileName: String): Boolean = { + private def isXmlFile(fileName: String): Boolean = { val xmlFileExtensions = Seq(".xml") xmlFileExtensions.exists(fileName.endsWith) } - protected def isGradleFile(fileName: String): Boolean = { + private def isGradleFile(fileName: String): Boolean = { val gradleRelatedFiles = Seq("build.gradle", "settings.gradle", "gradle.properties", "build.gradle.kts") gradleRelatedFiles.exists(fileName.endsWith) } - protected def isKotlinScript(fileName: String): Boolean = { + private def isKotlinScript(fileName: String): Boolean = { val ktsExtensions = Seq(".kts") ktsExtensions.exists(fileName.endsWith) } @@ -85,5 +74,3 @@ object SourceFilesPicker { } } } - -class SourceFilesPicker {} diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/interop/JavasrcInterop.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/interop/JavasrcInterop.scala index 75e52f13e160..b60cd6459e3d 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/interop/JavasrcInterop.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/interop/JavasrcInterop.scala @@ -1,11 +1,8 @@ package io.joern.kotlin2cpg.interop -import better.files.File import io.joern.javasrc2cpg.passes.{AstCreationPass => JavaSrcAstCreationPass} -import io.joern.javasrc2cpg.{Config => JavaSrcConfig} import io.joern.javasrc2cpg.JavaSrc2Cpg import io.shiftleft.codepropertygraph.Cpg -import org.slf4j.LoggerFactory object JavasrcInterop { def astCreationPass(inputPath: String, paths: List[String], cpg: Cpg): JavaSrcAstCreationPass = { diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/jar4import/Service.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/jar4import/Service.scala index 0ef2132283b1..b01a4e86ecd5 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/jar4import/Service.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/jar4import/Service.scala @@ -5,8 +5,7 @@ import org.slf4j.LoggerFactory class Service(url: String) { private val logger = LoggerFactory.getLogger(getClass) - val findUrl: String = url + "/find" - val healthUrl: String = url + "/health" + private val findUrl: String = url + "/find" def fetchDependencyCoordinates(imports: Seq[String]): Seq[String] = { try { diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/jar4import/UsesService.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/jar4import/UsesService.scala index 63549e8629ed..69c7ce4d5bba 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/jar4import/UsesService.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/jar4import/UsesService.scala @@ -2,11 +2,13 @@ package io.joern.kotlin2cpg.jar4import import com.squareup.tools.maven.resolution.ArtifactResolver import io.joern.kotlin2cpg.Kotlin2Cpg +import org.slf4j.LoggerFactory import java.net.{MalformedURLException, URL} -trait UsesService { - this: Kotlin2Cpg => +trait UsesService { this: Kotlin2Cpg => + + private val logger = LoggerFactory.getLogger(getClass) protected def reachableServiceMaybe(serviceUrl: String): Option[Service] = { try { @@ -40,9 +42,9 @@ trait UsesService { val resolver = new ArtifactResolver() val artifacts = coordinates.map { coordinate => - val strippedCoord = coordinate.stripPrefix("\"").stripSuffix("\"") - val result = resolver.download(strippedCoord, true) - logger.debug(s"Downloaded artifact for coordinate `$strippedCoord`.") + val strippedCoordinate = coordinate.stripPrefix("\"").stripSuffix("\"") + val result = resolver.download(strippedCoordinate, true) + logger.debug(s"Downloaded artifact for coordinate `$strippedCoordinate`.") result.component2().toAbsolutePath.toString } logger.info(s"Using `${artifacts.size}` dependencies.") diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/passes/ConfigPass.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/passes/ConfigPass.scala index 272ffa76eccf..3d90b4afcc8a 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/passes/ConfigPass.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/passes/ConfigPass.scala @@ -15,12 +15,7 @@ class ConfigPass(fileContentsAtPath: Iterable[FileContentAtPath], cpg: Cpg) fileContentsAtPath.map { entry => entry.filename }.toArray override def runOnPart(diffGraph: DiffGraphBuilder, fileName: String): Unit = { - val contentsAtPath = fileContentsAtPath - .filter { entry => - entry.filename == fileName - } - .toList - .headOption + val contentsAtPath = fileContentsAtPath.find(_.filename == fileName) contentsAtPath match { case Some(fm) => val configNode = NewConfigFile().name(fm.relativizedPath).content(fm.content) diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/passes/DependenciesFromMavenCoordinatesPass.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/passes/DependenciesFromMavenCoordinatesPass.scala index 7ffe4e2f9a9b..eb64a1510016 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/passes/DependenciesFromMavenCoordinatesPass.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/passes/DependenciesFromMavenCoordinatesPass.scala @@ -2,9 +2,7 @@ package io.joern.kotlin2cpg.passes import io.shiftleft.codepropertygraph.Cpg import io.shiftleft.passes.CpgPass -import io.shiftleft.semanticcpg.language._ -import io.shiftleft.codepropertygraph.generated.nodes.{NewDependency} -import org.slf4j.{Logger, LoggerFactory} +import io.shiftleft.codepropertygraph.generated.nodes.NewDependency import scala.util.matching.Regex @@ -25,10 +23,10 @@ org.springframework:spring-context:6.0.7 ``` */ class DependenciesFromMavenCoordinatesPass(coordinates: Seq[String], cpg: Cpg) extends CpgPass(cpg) { - override def run(dstGraph: DiffGraphBuilder): Unit = { + private val keyValPattern: Regex = "^([^:]+):([^:]+):([^:]+)$".r + override def run(dstGraph: DiffGraphBuilder): Unit = { coordinates.foreach { coordinate => - val keyValPattern: Regex = "^([^:]+):([^:]+):([^:]+)$".r for (patternMatch <- keyValPattern.findAllMatchIn(coordinate)) { val groupId = patternMatch.group(1) val name = patternMatch.group(2) diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/psi/PsiUtils.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/psi/PsiUtils.scala index f44cd392516c..bd1afd793cf1 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/psi/PsiUtils.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/psi/PsiUtils.scala @@ -18,8 +18,8 @@ object PsiUtils { expr.getEntries.asScala.filterNot(_.getText == underscore).toSeq } - def objectIdxMaybe(psiElem: PsiElement, containing: PsiElement) = { - class ForEachTreeVisitor(block: (KtElement) => Unit) extends KtTreeVisitorVoid { + def objectIdxMaybe(psiElem: PsiElement, containing: PsiElement): Option[Int] = { + class ForEachTreeVisitor(block: KtElement => Unit) extends KtTreeVisitorVoid { override def visitKtElement(element: KtElement): Unit = { if (element != null) { super.visitKtElement(element) @@ -30,12 +30,9 @@ object PsiUtils { val buf = scala.collection.mutable.ListBuffer.empty[KtObjectDeclaration] val visitor = - new ForEachTreeVisitor({ elem => - elem match { - case e: KtObjectDeclaration => - buf.append(e) - case _ => - } + new ForEachTreeVisitor({ + case e: KtObjectDeclaration => buf.append(e) + case _ => }) visitor.visitKtElement(containing.asInstanceOf[KtElement]) var outIdx: Option[Int] = None diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/DefaultTypeInfoProvider.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/DefaultTypeInfoProvider.scala index 5f11fee54f38..430c5255a55e 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/DefaultTypeInfoProvider.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/DefaultTypeInfoProvider.scala @@ -3,7 +3,6 @@ package io.joern.kotlin2cpg.types import io.joern.kotlin2cpg.psi.PsiUtils import io.joern.x2cpg.Defines import io.shiftleft.codepropertygraph.generated.Operators -import io.shiftleft.passes.KeyPool import org.jetbrains.kotlin.cli.jvm.compiler.{ KotlinCoreEnvironment, KotlinToJVMBytecodeCompiler, @@ -54,7 +53,7 @@ import org.jetbrains.kotlin.psi.{ KtTypeAlias, KtTypeReference } -import org.jetbrains.kotlin.resolve.{BindingContext, DescriptorToSourceUtils, DescriptorUtils} +import org.jetbrains.kotlin.resolve.{BindingContext, DescriptorUtils} import org.jetbrains.kotlin.resolve.DescriptorUtils.getSuperclassDescriptors import org.jetbrains.kotlin.resolve.`lazy`.descriptors.LazyClassDescriptor import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor @@ -87,7 +86,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn } } - def isValidRender(render: String): Boolean = { + private def isValidRender(render: String): Boolean = { !render.contains("ERROR") } @@ -152,7 +151,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn val renderedParameterTypes = relevantDesc.getValueParameters.asScala.toSeq - .map(renderTypeForParameterDesc(_)) + .map(renderTypeForParameterDesc) .mkString(",") val signature = s"$returnTypeFullName($renderedParameterTypes)" val fullName = s"$renderedFqName:$signature" @@ -257,7 +256,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn .getOrElse(defaultValue) } - def anonymousObjectIdx(obj: KtElement): Option[Int] = { + private def anonymousObjectIdx(obj: KtElement): Option[Int] = { val parentFn = KtPsiUtil.getTopmostParentOfTypes(obj, classOf[KtNamedFunction]) val containingObj = Option(parentFn).getOrElse(obj.getContainingKtFile) PsiUtils.objectIdxMaybe(obj, containingObj) @@ -368,7 +367,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn descMaybe.collect { case desc: FunctionDescriptor => desc } } - def isConstructorDescriptor(desc: FunctionDescriptor): Boolean = { + private def isConstructorDescriptor(desc: FunctionDescriptor): Boolean = { desc match { case _: JavaClassConstructorDescriptor => true case _: ClassConstructorDescriptorImpl => true @@ -406,7 +405,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn else renderedReturnType(relevantDesc.getOriginal) val renderedParameterTypes = relevantDesc.getValueParameters.asScala.toSeq - .map(renderTypeForParameterDesc(_)) + .map(renderTypeForParameterDesc) .mkString(",") val signature = s"$returnTypeFullName($renderedParameterTypes)" @@ -513,9 +512,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn } def isExtensionFn(fn: KtNamedFunction): Boolean = { - Option(bindingContext.get(BindingContext.FUNCTION, fn)) - .map(DescriptorUtils.isExtension(_)) - .getOrElse(false) + Option(bindingContext.get(BindingContext.FUNCTION, fn)).exists(DescriptorUtils.isExtension) } private def renderTypeForParameterDesc(p: ValueParameterDescriptor): String = { @@ -526,7 +523,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn .map(_.toList) .getOrElse(List()) if (typeUpperBounds.nonEmpty) - TypeRenderer.render(typeUpperBounds(0)) + TypeRenderer.render(typeUpperBounds.head) else TypeRenderer.render(p.getOriginal.getType) } @@ -553,7 +550,11 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn val rendered = if (renderedFqNameForDesc.startsWith(TypeConstants.kotlinApplyPrefix)) TypeConstants.javaLangObject else if (typeUpperBounds.size == 1) { - TypeRenderer.render(typeUpperBounds(0), shouldMapPrimitiveArrayTypes = false, unwrapPrimitives = false) + TypeRenderer.render( + typeUpperBounds.head, + shouldMapPrimitiveArrayTypes = false, + unwrapPrimitives = false + ) } else TypeRenderer.render(erpType, shouldMapPrimitiveArrayTypes = false, unwrapPrimitives = false) s"$rendered.${originalDesc.getName}" } @@ -572,7 +573,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn val renderedParameterTypes = originalDesc.getValueParameters.asScala.toSeq - .map(renderTypeForParameterDesc(_)) + .map(renderTypeForParameterDesc) .mkString(",") val renderedReturnType = if (isConstructorDescriptor(originalDesc)) TypeConstants.void @@ -616,7 +617,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn } } - def lambdaInvocationSignature(expr: KtLambdaExpression, returnType: String): String = { + private def lambdaInvocationSignature(expr: KtLambdaExpression, returnType: String): String = { val hasImplicitParameter = implicitParameterName(expr) val params = expr.getValueParameters val paramsString = @@ -625,7 +626,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn else if (params.size() == 1) TypeConstants.javaLangObject else s"${TypeConstants.javaLangObject}${("," + TypeConstants.javaLangObject) * (expr.getValueParameters.size() - 1)}" - s"${returnType}($paramsString)" + s"$returnType($paramsString)" } def parameterType(parameter: KtParameter, defaultValue: String): String = { @@ -643,7 +644,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn .getOrElse(List()) render = if (typeUpperBounds.nonEmpty) - TypeRenderer.render(typeUpperBounds(0)) + TypeRenderer.render(typeUpperBounds.head) else TypeRenderer.render(variableDesc.getType) if isValidRender(render) && !variableDesc.getType.isInstanceOf[ErrorType] @@ -929,12 +930,10 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn } val hasSingleImplicitParameter = - Option(bindingContext.get(BindingContext.EXPECTED_EXPRESSION_TYPE, expr)) - .map { desc => - // 1 for the parameter + 1 for the return type == 2 - desc.getConstructor.getParameters.size() == 2 - } - .getOrElse(false) + Option(bindingContext.get(BindingContext.EXPECTED_EXPRESSION_TYPE, expr)).exists { desc => + // 1 for the parameter + 1 for the return type == 2 + desc.getConstructor.getParameters.size() == 2 + } val containingQualifiedExpression = Option(expr.getParent) .map(_.getParent) @@ -969,7 +968,7 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn object DefaultTypeInfoProvider { private val logger = LoggerFactory.getLogger(getClass) - def bindingsForEntity(bindings: BindingContext, entity: KtElement): KeyFMap = { + private def bindingsForEntity(bindings: BindingContext, entity: KtElement): KeyFMap = { try { val thisField = bindings.getClass.getDeclaredField("this$0") thisField.setAccessible(true) @@ -997,7 +996,7 @@ object DefaultTypeInfoProvider { } } - def bindingsForEntityAsString(bindings: BindingContext, entity: KtElement): String = { + private def bindingsForEntityAsString(bindings: BindingContext, entity: KtElement): String = { val mapForEntity = bindingsForEntity(bindings, entity) if (mapForEntity != null) { val keys = mapForEntity.getKeys diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/TypeInfoProvider.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/TypeInfoProvider.scala index f963aeb93144..84220bf5a0ef 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/TypeInfoProvider.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/TypeInfoProvider.scala @@ -1,6 +1,5 @@ package io.joern.kotlin2cpg.types -import io.shiftleft.passes.KeyPool import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.psi.{ KtAnnotationEntry, @@ -12,7 +11,6 @@ import org.jetbrains.kotlin.psi.{ KtElement, KtExpression, KtFile, - KtLambdaArgument, KtLambdaExpression, KtNameReferenceExpression, KtNamedFunction, diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/TypeRenderer.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/TypeRenderer.scala index 0bdfc2cfd015..716c5e0a0631 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/TypeRenderer.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/types/TypeRenderer.scala @@ -28,7 +28,7 @@ object TypeRenderer { "kotlin.ShortArray" -> "short[]" ) - def descriptorRenderer(): DescriptorRenderer = { + private def descriptorRenderer(): DescriptorRenderer = { val opts = new DescriptorRendererOptionsImpl opts.setParameterNamesInFunctionalTypes(false) opts.setInformativeErrorType(false) @@ -64,11 +64,9 @@ object TypeRenderer { } val strippedOfContainingDeclarationIfNeeded = Option(desc.getContainingDeclaration) - .map { containingDeclaration => - containingDeclaration match { - case c: ClassDescriptor => maybeReplacedOrTake(c, simpleRender) - case _ => simpleRender - } + .map { + case c: ClassDescriptor => maybeReplacedOrTake(c, simpleRender) + case _ => simpleRender } .getOrElse(simpleRender) desc match { diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/DefaultRegisteredTypesTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/DefaultRegisteredTypesTests.scala index 0f2b60b7b24f..1978c0e3d840 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/DefaultRegisteredTypesTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/DefaultRegisteredTypesTests.scala @@ -4,6 +4,7 @@ import io.joern.kotlin2cpg.testfixtures.KotlinCode2CpgFixture import io.shiftleft.semanticcpg.language._ class DefaultRegisteredTypesTests extends KotlinCode2CpgFixture(withOssDataflow = false) { + "CPG for code with simple user-defined class" should { lazy val cpg = code(""" |package mypkg @@ -15,4 +16,5 @@ class DefaultRegisteredTypesTests extends KotlinCode2CpgFixture(withOssDataflow cpg.typ.fullNameExact("java.lang.Object").size shouldBe 1 } } + } diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/SourceFilesPickerTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/SourceFilesPickerTests.scala index 5637da8f2f43..730b34aa9ee4 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/SourceFilesPickerTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/SourceFilesPickerTests.scala @@ -7,10 +7,12 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.BeforeAndAfterAll class SourceFilesPickerTests extends AnyFreeSpec with Matchers with BeforeAndAfterAll { + "SourceFilesPicker" - { "should not filter out fileNames ending in `build.gradle`" in { val inFileName = "/path/does/not/exist/build.gradle" SourceFilesPicker.shouldFilter(inFileName) shouldBe false } } + } diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/compiler/CompilerAPITests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/compiler/CompilerAPITests.scala index 3773a9617a34..d7c78defaa52 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/compiler/CompilerAPITests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/compiler/CompilerAPITests.scala @@ -6,19 +6,17 @@ import io.joern.kotlin2cpg.DefaultContentRootJarPath import io.joern.kotlin2cpg.Kotlin2Cpg import io.joern.x2cpg.utils.ExternalCommand import io.joern.x2cpg.Defines +import io.shiftleft.semanticcpg.language.* import io.shiftleft.utils.ProjectRoot - -import java.nio.file.Paths -import org.jetbrains.kotlin.cli.common.messages.{ - CompilerMessageSeverity, - CompilerMessageSourceLocation, - MessageCollector -} +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation +import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers import org.slf4j.LoggerFactory -import io.shiftleft.semanticcpg.language.* + +import java.nio.file.Paths class CompilerAPITests extends AnyFreeSpec with Matchers { diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/config/ConfigTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/config/ConfigTests.scala index 061083f15920..7aa57c3fa9e4 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/config/ConfigTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/config/ConfigTests.scala @@ -35,9 +35,7 @@ class ConfigTests extends AnyWordSpec with Matchers with Inside { "--include-java-sources" ) - def getSuffix(s: String, n: Int): String = { - s.reverse.take(n).reverse - } + def getSuffix(s: String, n: Int): String = s.takeRight(n) inside(X2Cpg.parseCommandLine(args, parser, Config())) { case Some(config) => config.inputPath.endsWith("INPUT") shouldBe true diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/testfixtures/KotlinCodeToCpgFixture.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/testfixtures/KotlinCodeToCpgFixture.scala index 0ef23ea8c2f6..afe8dbc0c4d1 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/testfixtures/KotlinCodeToCpgFixture.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/testfixtures/KotlinCodeToCpgFixture.scala @@ -2,15 +2,15 @@ package io.joern.kotlin2cpg.testfixtures import better.files.File as BFile import io.joern.dataflowengineoss.language.* -import io.joern.dataflowengineoss.layers.dataflows.{OssDataFlow, OssDataFlowOptions} -import io.joern.dataflowengineoss.queryengine.EngineContext import io.joern.dataflowengineoss.semanticsloader.FlowSemantic -import io.joern.dataflowengineoss.testfixtures.{SemanticCpgTestFixture, SemanticTestCpg} -import io.joern.kotlin2cpg.{Config, Kotlin2Cpg} -import io.joern.x2cpg.X2Cpg -import io.joern.x2cpg.testfixtures.{Code2CpgFixture, DefaultTestCpg, LanguageFrontend, TestCpg} +import io.joern.dataflowengineoss.testfixtures.SemanticCpgTestFixture +import io.joern.dataflowengineoss.testfixtures.SemanticTestCpg +import io.joern.kotlin2cpg.Config +import io.joern.kotlin2cpg.Kotlin2Cpg +import io.joern.x2cpg.testfixtures.Code2CpgFixture +import io.joern.x2cpg.testfixtures.DefaultTestCpg +import io.joern.x2cpg.testfixtures.LanguageFrontend import io.shiftleft.codepropertygraph.Cpg -import io.shiftleft.semanticcpg.layers.LayerCreatorContext import io.shiftleft.utils.ProjectRoot import java.io.File diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/ArgumentIndexTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/ArgumentIndexTests.scala index 266cf2e7e8ad..83ae136ef9e0 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/ArgumentIndexTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/ArgumentIndexTests.scala @@ -1,8 +1,9 @@ package io.joern.kotlin2cpg.validation import io.joern.kotlin2cpg.testfixtures.KotlinCode2CpgFixture -import io.shiftleft.codepropertygraph.generated.nodes.{Call, ControlStructure, Literal} -import io.shiftleft.semanticcpg.language._ +import io.shiftleft.codepropertygraph.generated.nodes.Call +import io.shiftleft.codepropertygraph.generated.nodes.Literal +import io.shiftleft.semanticcpg.language.* class ArgumentIndexTests extends KotlinCode2CpgFixture(withOssDataflow = false) { "CPG for code with simple if-expression inside DQE" should { diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/NestedDeclarationTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/NestedDeclarationsTests.scala similarity index 91% rename from joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/NestedDeclarationTests.scala rename to joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/NestedDeclarationsTests.scala index 3dd845182b16..e622e3ce0d45 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/NestedDeclarationTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/NestedDeclarationsTests.scala @@ -1,12 +1,9 @@ package io.joern.kotlin2cpg.validation import io.joern.kotlin2cpg.testfixtures.KotlinCode2CpgFixture -import io.shiftleft.codepropertygraph.generated.Operators -import io.shiftleft.codepropertygraph.generated.edges.Ast -import io.shiftleft.codepropertygraph.generated.nodes.{ClosureBinding, Method, TypeDecl} -import io.shiftleft.codepropertygraph.generated.DispatchTypes -import io.shiftleft.semanticcpg.language._ -import overflowdb.traversal.jIteratortoTraversal +import io.shiftleft.codepropertygraph.generated.nodes.Method +import io.shiftleft.codepropertygraph.generated.nodes.TypeDecl +import io.shiftleft.semanticcpg.language.* class NestedDeclarationsTests extends KotlinCode2CpgFixture(withOssDataflow = false) { "CPG for code with a simple local fn declaration" should { diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/UnitTypeMappingTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/UnitTypeMappingTests.scala index 4dd3562dc3c6..dc2f0f467378 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/UnitTypeMappingTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/UnitTypeMappingTests.scala @@ -4,7 +4,8 @@ import io.joern.kotlin2cpg.testfixtures.KotlinCode2CpgFixture import io.shiftleft.semanticcpg.language._ class UnitTypeMappingTests extends KotlinCode2CpgFixture(withOssDataflow = false) { - "CPG for code with definion of simple fn returning `kotlin.Unit`" should { + + "CPG for code with definition of simple fn returning `kotlin.Unit`" should { lazy val cpg = code(""" |package mypkg | @@ -18,4 +19,5 @@ class UnitTypeMappingTests extends KotlinCode2CpgFixture(withOssDataflow = false m.fullName shouldBe "mypkg.main:void()" } } + } diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/ValidationTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/ValidationTests.scala index 10b99faae02d..2f8d27ee235b 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/ValidationTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/validation/ValidationTests.scala @@ -2,11 +2,9 @@ package io.joern.kotlin2cpg.validation import io.joern.kotlin2cpg.testfixtures.KotlinCode2CpgFixture import io.shiftleft.codepropertygraph.generated.Operators -import io.shiftleft.codepropertygraph.generated.edges.Ast import io.shiftleft.codepropertygraph.generated.nodes.ClosureBinding import io.shiftleft.codepropertygraph.generated.DispatchTypes -import io.shiftleft.semanticcpg.language._ -import overflowdb.traversal.jIteratortoTraversal +import io.shiftleft.semanticcpg.language.* class ValidationTests extends KotlinCode2CpgFixture(withOssDataflow = false) { "CPG for code with lambdas with no params and one param" should { diff --git a/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/utils/dependency/DependencyResolver.scala b/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/utils/dependency/DependencyResolver.scala index 440d39886afc..4fd0d93df7b5 100644 --- a/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/utils/dependency/DependencyResolver.scala +++ b/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/utils/dependency/DependencyResolver.scala @@ -64,7 +64,7 @@ object DependencyResolver { def getDependencies( projectDir: Path, params: DependencyResolverParams = new DependencyResolverParams - ): Option[collection.Seq[String]] = { + ): Option[Seq[String]] = { val dependencies = findSupportedBuildFiles(projectDir).flatMap { buildFile => if (isMavenBuildFile(buildFile)) { MavenDependencies.get(buildFile.getParent)