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 1435c0118414..d806897cf6f0 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,8 +2,6 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants import io.joern.kotlin2cpg.KtFileWithMeta -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 @@ -15,6 +13,7 @@ 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.x2cpg.utils.NodeBuilders import io.joern.x2cpg.utils.NodeBuilders.newMethodReturnNode import io.shiftleft.codepropertygraph.generated.* import io.shiftleft.codepropertygraph.generated.nodes.* @@ -292,19 +291,19 @@ class AstCreator(fileWithMeta: KtFileWithMeta, xTypeInfoProvider: TypeInfoProvid for { node <- importAsts.flatMap(_.root.collectAll[NewImport]) name = getName(node) - } yield Ast(namespaceBlockNode(name, name, relativizedPath)) + } yield Ast(NodeBuilders.newNamespaceBlockNode(name, name, relativizedPath)) val packageName = ktFile.getPackageFqName.toString val node = if (packageName == Constants.root) - namespaceBlockNode( + NodeBuilders.newNamespaceBlockNode( NamespaceTraversal.globalNamespaceName, NamespaceTraversal.globalNamespaceName, relativizedPath ) else { val name = packageName.split("\\.").lastOption.getOrElse("") - namespaceBlockNode(name, packageName, relativizedPath) + NodeBuilders.newNamespaceBlockNode(name, packageName, relativizedPath) } methodAstParentStack.push(node) @@ -418,7 +417,7 @@ class AstCreator(fileWithMeta: KtFileWithMeta, xTypeInfoProvider: TypeInfoProvid val componentNAst = callAst(componentNCallNode, Seq(), Option(componentNIdentifierAst)) - val assignmentCallNode = operatorCallNode( + val assignmentCallNode = NodeBuilders.newOperatorCallNode( Operators.assignment, s"${entry.getText} = $componentNCallCode", None, 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 36e43dc8ab6c..b9474e5b8415 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 @@ -1,20 +1,20 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants -import io.joern.kotlin2cpg.ast.Nodes.modifierNode -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 import io.joern.kotlin2cpg.types.TypeConstants import io.joern.kotlin2cpg.types.TypeInfoProvider import io.joern.x2cpg.Ast +import io.joern.x2cpg.datastructures.Stack.* import io.joern.x2cpg.Defines import io.joern.x2cpg.ValidationMode import io.joern.x2cpg.utils.NodeBuilders import io.joern.x2cpg.utils.NodeBuilders.newBindingNode import io.joern.x2cpg.utils.NodeBuilders.newIdentifierNode import io.joern.x2cpg.utils.NodeBuilders.newMethodReturnNode +import io.joern.x2cpg.utils.NodeBuilders.newModifierNode import io.shiftleft.codepropertygraph.generated.DispatchTypes import io.shiftleft.codepropertygraph.generated.EdgeTypes import io.shiftleft.codepropertygraph.generated.Operators @@ -63,6 +63,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { val outBaseTypeFullNames = Option(baseTypeFullNames).filter(_.nonEmpty).getOrElse(Seq(TypeConstants.javaLangObject)) val typeDecl = typeDeclNode(ktClass, className, classFullName, relativizedPath, outBaseTypeFullNames, None) scope.pushNewScope(typeDecl) + methodAstParentStack.push(typeDecl) val primaryCtor = ktClass.getPrimaryConstructor val constructorParams = ktClass.getPrimaryConstructorParameters.asScala.toList @@ -100,12 +101,12 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { val thisAst = astWithRefEdgeMaybe(Constants.this_, thisIdentifier) val fieldIdentifier = fieldIdentifierNode(decl, decl.getName, decl.getName) - val fieldAccessCall = - operatorCallNode(Operators.fieldAccess, s"${Constants.this_}.${fieldIdentifier.canonicalName}", None) + val fieldAccessCall = NodeBuilders + .newOperatorCallNode(Operators.fieldAccess, s"${Constants.this_}.${fieldIdentifier.canonicalName}", None) val fieldAccessCallAst = callAst(fieldAccessCall, List(thisAst, Ast(fieldIdentifier))) - val assignmentNode = - operatorCallNode(Operators.assignment, s"${fieldAccessCall.code} = ${decl.getInitializer.getText}") + val assignmentNode = NodeBuilders + .newOperatorCallNode(Operators.assignment, s"${fieldAccessCall.code} = ${decl.getInitializer.getText}") callAst(assignmentNode, List(fieldAccessCallAst, rhsAst)) } @@ -125,7 +126,8 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { blockNode(ktClass, "", TypeConstants.void), memberSetCalls ++ memberInitializerSetCalls ++ anonymousInitAsts ), - constructorMethodReturn + constructorMethodReturn, + Seq(newModifierNode(ModifierTypes.CONSTRUCTOR)) ) val node = newBindingNode(primaryCtorMethodNode.name, primaryCtorMethodNode.signature, primaryCtorMethodNode.fullName) @@ -183,7 +185,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { val annotationAsts = ktClass.getAnnotationEntries.asScala.map(astForAnnotationEntry).toSeq - val modifiers = if (isAbstract(ktClass)) List(Ast(modifierNode(ModifierTypes.ABSTRACT))) else Nil + val modifiers = if (isAbstract(ktClass)) List(Ast(NodeBuilders.newModifierNode(ModifierTypes.ABSTRACT))) else Nil val children = methodAsts ++ List(constructorAst) ++ membersFromPrimaryCtorAsts ++ secondaryConstructorAsts ++ _componentNMethodAsts.toList ++ memberAsts ++ annotationAsts ++ modifiers @@ -209,6 +211,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { ast } val companionObjectAsts = ktClass.getCompanionObjects.asScala.flatMap(astsForClassOrObject(_, None)) + methodAstParentStack.pop() scope.popScope() Seq(finalAst.withChildren(annotations.map(astForAnnotationEntry))) ++ companionObjectAsts ++ innerTypeDeclAsts @@ -226,11 +229,11 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { val fieldIdentifier = fieldIdentifierNode(param, paramName, paramName) val fieldAccessCall = - operatorCallNode(Operators.fieldAccess, s"${Constants.this_}.$paramName", Option(typeFullName)) + NodeBuilders.newOperatorCallNode(Operators.fieldAccess, s"${Constants.this_}.$paramName", Option(typeFullName)) val fieldAccessCallAst = callAst(fieldAccessCall, List(thisAst, Ast(fieldIdentifier))) val assignmentNode = - operatorCallNode(Operators.assignment, s"${fieldAccessCall.code} = ${paramIdentifier.code}") + NodeBuilders.newOperatorCallNode(Operators.assignment, s"${fieldAccessCall.code} = ${paramIdentifier.code}") callAst(assignmentNode, List(fieldAccessCallAst, paramIdentifierAst)) } @@ -275,15 +278,15 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { val assignmentLhsAst = Ast(assignmentLhsNode).withRefEdge(assignmentLhsNode, localForTmpNode) val tmpAssignmentAst = if (isCtor) { - val assignmentRhsNode = - operatorCallNode( - Operators.alloc, - Constants.alloc, - Option(localForTmpNode.typeFullName), - line(expr), - column(expr) - ) - val assignmentNode = operatorCallNode(Operators.assignment, s"$tmpName = ${Constants.alloc}", None) + val assignmentRhsNode = NodeBuilders.newOperatorCallNode( + Operators.alloc, + Constants.alloc, + Option(localForTmpNode.typeFullName), + line(expr), + column(expr) + ) + val assignmentNode = + NodeBuilders.newOperatorCallNode(Operators.assignment, s"$tmpName = ${Constants.alloc}", None) callAst(assignmentNode, List(assignmentLhsAst, Ast(assignmentRhsNode))) } else { expr.getInitializer match { @@ -296,7 +299,8 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { case expression: KtIfExpression => astForIfAsExpression(expression, None, None) case _ => - val assignmentNode = operatorCallNode(Operators.assignment, s"$tmpName = ${rhsCall.getText}", None) + val assignmentNode = + NodeBuilders.newOperatorCallNode(Operators.assignment, s"$tmpName = ${rhsCall.getText}", None) val assignmentRhsAst = astsForExpression(rhsCall, None).headOption.getOrElse(Ast(unknownNode(rhsCall, Constants.empty))) callAst(assignmentNode, List(assignmentLhsAst, assignmentRhsAst)) @@ -393,8 +397,11 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { val thisAst = Ast(thisIdentifier).withRefEdge(thisIdentifier, thisParam) val fieldIdentifier = fieldIdentifierNode(valueParam, valueParam.getName, valueParam.getName) - val fieldAccessCall = - operatorCallNode(Operators.fieldAccess, s"${Constants.this_}.${valueParam.getName}", Option(typeFullName)) + val fieldAccessCall = NodeBuilders.newOperatorCallNode( + Operators.fieldAccess, + s"${Constants.this_}.${valueParam.getName}", + Option(typeFullName) + ) val fieldAccessCallAst = callAst(fieldAccessCall, List(thisAst, Ast(fieldIdentifier))) val methodBlockAst = blockAst( blockNode(valueParam, fieldAccessCall.code, typeFullName), @@ -452,7 +459,8 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { secondaryCtorMethodNode, constructorParamsAsts, ctorMethodBlockAsts.headOption.getOrElse(Ast(unknownNode(ctor.getBodyExpression, Constants.empty))), - ctorMethodReturnNode + ctorMethodReturnNode, + Seq(newModifierNode(ModifierTypes.CONSTRUCTOR)) ) } } @@ -482,13 +490,18 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { scope.addToScope(tmpName, localForTmp) val localAst = Ast(localForTmp) - val rhsAst = Ast(operatorCallNode(Operators.alloc, Operators.alloc, None)) + val rhsAst = Ast(NodeBuilders.newOperatorCallNode(Operators.alloc, Operators.alloc, None)) val identifier = identifierNode(expr, tmpName, tmpName, localForTmp.typeFullName) val identifierAst = astWithRefEdgeMaybe(identifier.name, identifier) - val assignmentNode = - operatorCallNode(Operators.assignment, s"${identifier.name} = ", None, line(expr), column(expr)) + val assignmentNode = NodeBuilders.newOperatorCallNode( + Operators.assignment, + s"${identifier.name} = ", + None, + line(expr), + column(expr) + ) val assignmentCallAst = callAst(assignmentNode, List(identifierAst) ++ List(rhsAst)) val initSignature = s"${TypeConstants.void}()" val initFullName = s"$typeDeclFullName.${TypeConstants.initPrefix}:$initSignature" @@ -552,12 +565,13 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { val typeFullName = registerType( typeInfoProvider.expressionType(expr.getDelegateExpressionOrInitializer, Defines.UnresolvedNamespace) ) - val rhsAst = Ast(operatorCallNode(Operators.alloc, Operators.alloc, Option(typeFullName))) + val rhsAst = Ast(NodeBuilders.newOperatorCallNode(Operators.alloc, Operators.alloc, Option(typeFullName))) val identifier = identifierNode(elem, elem.getText, elem.getText, local.typeFullName) val identifierAst = astWithRefEdgeMaybe(identifier.name, identifier) - val assignmentNode = operatorCallNode(Operators.assignment, expr.getText, None, line(expr), column(expr)) + val assignmentNode = + NodeBuilders.newOperatorCallNode(Operators.assignment, expr.getText, None, line(expr), column(expr)) val assignmentCallAst = callAst(assignmentNode, List(identifierAst) ++ List(rhsAst)) val (fullName, signature) = @@ -604,12 +618,13 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { val typeFullName = registerType( typeInfoProvider.expressionType(expr.getDelegateExpressionOrInitializer, Defines.UnresolvedNamespace) ) - val rhsAst = Ast(operatorCallNode(Operators.alloc, Operators.alloc, None)) + val rhsAst = Ast(NodeBuilders.newOperatorCallNode(Operators.alloc, Operators.alloc, None)) val identifier = identifierNode(elem, elem.getText, elem.getText, node.typeFullName) val identifierAst = astWithRefEdgeMaybe(identifier.name, identifier) - val assignmentNode = operatorCallNode(Operators.assignment, expr.getText, None, line(expr), column(expr)) + val assignmentNode = + NodeBuilders.newOperatorCallNode(Operators.assignment, expr.getText, None, line(expr), column(expr)) val assignmentCallAst = callAst(assignmentNode, List(identifierAst) ++ List(rhsAst)) val initSignature = s"${TypeConstants.void}()" val initFullName = s"$typeFullName${TypeConstants.initPrefix}:$initSignature" @@ -635,10 +650,11 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) { scope.addToScope(expr.getName, node) val localAst = Ast(node) - val rhsAsts = astsForExpression(expr.getDelegateExpressionOrInitializer, Some(2)) - val identifier = identifierNode(elem, elem.getText, elem.getText, typeFullName) - val identifierAst = astWithRefEdgeMaybe(identifier.name, identifier) - val assignmentNode = operatorCallNode(Operators.assignment, expr.getText, None, line(expr), column(expr)) + val rhsAsts = astsForExpression(expr.getDelegateExpressionOrInitializer, Some(2)) + val identifier = identifierNode(elem, elem.getText, elem.getText, typeFullName) + val identifierAst = astWithRefEdgeMaybe(identifier.name, identifier) + val assignmentNode = + NodeBuilders.newOperatorCallNode(Operators.assignment, expr.getText, None, line(expr), column(expr)) val call = callAst(assignmentNode, List(identifierAst) ++ rhsAsts) .withChildren(annotations.map(astForAnnotationEntry)) 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 f8f1da7843cb..5d13cfdcb47d 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 @@ -1,13 +1,13 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants -import io.joern.kotlin2cpg.ast.Nodes.operatorCallNode 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.joern.x2cpg.utils.NodeBuilders import io.shiftleft.codepropertygraph.generated.DispatchTypes import io.shiftleft.codepropertygraph.generated.Operators import io.shiftleft.codepropertygraph.generated.nodes.NewMethodRef @@ -121,7 +121,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { registerType(typeInfoProvider.containingDeclType(expr, TypeConstants.any)) val retType = registerType(typeInfoProvider.expressionType(expr, TypeConstants.any)) val node = withArgumentIndex( - operatorCallNode(Operators.fieldAccess, expr.getText, Option(retType), line(expr), column(expr)), + NodeBuilders.newOperatorCallNode(Operators.fieldAccess, expr.getText, Option(retType), line(expr), column(expr)), argIdx ).argumentName(argNameMaybe) callAst(node, List(receiverAst) ++ argAsts) @@ -203,12 +203,12 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { val localAst = Ast(local) val typeFullName = registerType(typeInfoProvider.expressionType(expr, Defines.UnresolvedNamespace)) - val rhsAst = Ast(operatorCallNode(Operators.alloc, Operators.alloc, Option(typeFullName))) + val rhsAst = Ast(NodeBuilders.newOperatorCallNode(Operators.alloc, Operators.alloc, Option(typeFullName))) val identifier = identifierNode(expr, localName, localName, local.typeFullName) val identifierAst = astWithRefEdgeMaybe(identifier.name, identifier) - val assignmentNode = operatorCallNode( + val assignmentNode = NodeBuilders.newOperatorCallNode( Operators.assignment, s"${identifier.name} = ${Operators.alloc}", None, @@ -313,7 +313,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { val node = withArgumentIndex( if (fullName.startsWith(".")) { - operatorCallNode(fullName, expr.getText, Option(retType), line(expr), column(expr)) + NodeBuilders.newOperatorCallNode(fullName, expr.getText, Option(retType), line(expr), column(expr)) } else { callNode(expr, expr.getText, methodName, fullName, dispatchType, Some(signature), Some(retType)) }, @@ -389,7 +389,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { registerType(typeInfoProvider.expressionType(expr, TypeConstants.any)) val args = astsForExpression(expr.getLeftHandSide, None) ++ Seq(astForTypeReference(expr.getTypeReference, None, argName)) - val node = operatorCallNode(Operators.is, expr.getText, None, line(expr), column(expr)) + val node = NodeBuilders.newOperatorCallNode(Operators.is, expr.getText, None, line(expr), column(expr)) callAst(withArgumentName(withArgumentIndex(node, argIdx), argName), args.toList) .withChildren(annotations.map(astForAnnotationEntry)) } @@ -402,7 +402,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { )(implicit typeInfoProvider: TypeInfoProvider): Ast = { registerType(typeInfoProvider.expressionType(expr, TypeConstants.any)) val args = astsForExpression(expr.getLeft, None) ++ Seq(astForTypeReference(expr.getRight, None, None)) - val node = operatorCallNode(Operators.cast, expr.getText, None, line(expr), column(expr)) + val node = NodeBuilders.newOperatorCallNode(Operators.cast, expr.getText, None, line(expr), column(expr)) callAst(withArgumentName(withArgumentIndex(node, argIdx), argName), args.toList) .withChildren(annotations.map(astForAnnotationEntry)) } @@ -490,11 +490,11 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { val tmpLocalAst = Ast(tmpLocalNode) val assignmentRhsNode = - operatorCallNode(Operators.alloc, Constants.alloc, Option(typeFullName), line(expr), column(expr)) + NodeBuilders.newOperatorCallNode(Operators.alloc, Constants.alloc, Option(typeFullName), line(expr), column(expr)) val assignmentLhsNode = identifierNode(expr, tmpName, tmpName, typeFullName) val assignmentLhsAst = astWithRefEdgeMaybe(tmpName, assignmentLhsNode) - val assignmentNode = operatorCallNode(Operators.assignment, Operators.assignment) + val assignmentNode = NodeBuilders.newOperatorCallNode(Operators.assignment, Operators.assignment) val assignmentAst = callAst(assignmentNode, List(assignmentLhsAst, Ast(assignmentRhsNode))) val initReceiverNode = identifierNode(expr, tmpName, tmpName, typeFullName) .argumentIndex(0) @@ -550,7 +550,8 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { val typeFullName = registerType(typeInfoProvider.expressionType(expr, TypeConstants.any)) val args = List(astsForExpression(expr.getBaseExpression, None).headOption.getOrElse(Ast())) .filterNot(_.root == null) - val node = operatorCallNode(operatorType, expr.getText, Option(typeFullName), line(expr), column(expr)) + val node = + NodeBuilders.newOperatorCallNode(operatorType, expr.getText, Option(typeFullName), line(expr), column(expr)) callAst(withArgumentName(withArgumentIndex(node, argIdx), argName), args) .withChildren(annotations.map(astForAnnotationEntry)) } @@ -571,7 +572,8 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { val typeFullName = registerType(typeInfoProvider.expressionType(expr, TypeConstants.any)) val args = List(astsForExpression(expr.getBaseExpression, None).headOption.getOrElse(Ast())) .filterNot(_.root == null) - val node = operatorCallNode(operatorType, expr.getText, Option(typeFullName), line(expr), column(expr)) + val node = + NodeBuilders.newOperatorCallNode(operatorType, expr.getText, Option(typeFullName), line(expr), column(expr)) callAst(withArgumentName(withArgumentIndex(node, argIdx), argName), args) .withChildren(annotations.map(astForAnnotationEntry)) } @@ -590,7 +592,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { astsForExpression(expr, Option(idx + 1)) } val callNode = - operatorCallNode( + NodeBuilders.newOperatorCallNode( Operators.indexAccess, expression.getText, Option(typeFullName), 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 d24b6b162ac2..ec6e0f013f61 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 @@ -1,7 +1,6 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants -import io.joern.kotlin2cpg.ast.Nodes.modifierNode import io.joern.kotlin2cpg.types.TypeConstants import io.joern.kotlin2cpg.types.TypeInfoProvider import io.joern.x2cpg.Ast @@ -16,6 +15,7 @@ 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.semanticcpg.language.types.structure.NamespaceTraversal import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.psi.* @@ -30,6 +30,38 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { typeInfoProvider.modality(ktFn).contains(Modality.ABSTRACT) } + private def createFunctionTypeAndTypeDeclAst( + node: KtNamedFunction, + methodNode: NewMethod, + parentNode: NewNode, + methodName: String, + methodFullName: String, + signature: String, + filename: String + ): Ast = { + val astParentType = parentNode.label + val astParentName = parentNode.properties(TypeDecl.Properties.Name.name).toString + val astParentFullName = parentNode.properties(TypeDecl.Properties.FullName.name).toString + val functionTypeDeclNode = + typeDeclNode( + node, + methodName, + methodFullName, + filename, + methodName, + astParentType = astParentType, + astParentFullName = astParentFullName, + Seq(TypeConstants.kotlinFunctionXPrefix) + ) + if (astParentName == NamespaceTraversal.globalNamespaceName || astParentType == Method.Label) { + // Bindings for others (classes, interfaces, and such) are already created in their respective CPG creation functions + val bindingNode = NewBinding().name(methodName).methodFullName(methodFullName).signature(signature) + Ast(functionTypeDeclNode).withBindsEdge(functionTypeDeclNode, bindingNode).withRefEdge(bindingNode, methodNode) + } else { + Ast(functionTypeDeclNode) + } + } + def astsForMethod(ktFn: KtNamedFunction, needsThisParameter: Boolean = false, withVirtualModifier: Boolean = false)( implicit typeInfoProvider: TypeInfoProvider ): Seq[Ast] = { @@ -84,18 +116,29 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { val visibility = typeInfoProvider.visibility(ktFn) val visibilityModifierType = modifierTypeForVisibility(visibility.getOrElse(DescriptorVisibilities.UNKNOWN)) - val visibilityModifier = modifierNode(visibilityModifierType) + val visibilityModifier = NodeBuilders.newModifierNode(visibilityModifierType) val modifierNodes = - if (withVirtualModifier) Seq(modifierNode(ModifierTypes.VIRTUAL)) + if (withVirtualModifier) Seq(NodeBuilders.newModifierNode(ModifierTypes.VIRTUAL)) else Seq() val modifiers = if (isAbstract(ktFn)) { - List(visibilityModifier) ++ modifierNodes :+ modifierNode(ModifierTypes.ABSTRACT) + List(visibilityModifier) ++ modifierNodes :+ NodeBuilders.newModifierNode(ModifierTypes.ABSTRACT) } else { List(visibilityModifier) ++ modifierNodes } + val functionTypeAndTypeDeclAst = createFunctionTypeAndTypeDeclAst( + ktFn, + _methodNode, + methodAstParentStack.head, + ktFn.getName, + fullName, + signature, + relativizedPath + ) + Ast.storeInDiffGraph(functionTypeAndTypeDeclAst, diffGraph) + val annotationEntries = ktFn.getAnnotationEntries.asScala.map(astForAnnotationEntry).toSeq Seq( methodAst(_methodNode, thisParameterAsts ++ methodParametersAsts, bodyAst, _methodReturnNode, modifiers) @@ -105,24 +148,23 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { } def astForParameter(param: KtParameter, order: Int)(implicit typeInfoProvider: TypeInfoProvider): Ast = { - - val name = - if (param.getDestructuringDeclaration != null) - Constants.paramNameLambdaDestructureDecl - else - param.getName + val name = if (param.getDestructuringDeclaration != null) { + Constants.paramNameLambdaDestructureDecl + } else { + param.getName + } val explicitTypeName = Option(param.getTypeReference).map(_.getText).getOrElse(TypeConstants.any) val typeFullName = registerType(typeInfoProvider.parameterType(param, explicitTypeName)) - val node = - parameterInNode(param, name, name, order, false, EvaluationStrategies.BY_VALUE, typeFullName) + val node = parameterInNode(param, name, name, order, false, EvaluationStrategies.BY_VALUE, typeFullName) scope.addToScope(name, node) val annotations = param.getAnnotationEntries.asScala.map(astForAnnotationEntry).toSeq - Ast(node) - .withChildren(annotations) + Ast(node).withChildren(annotations) } + private case class NodeContext(node: NewNode, name: String, typeFullName: String) + def astForAnonymousFunction( fn: KtNamedFunction, argIdxMaybe: Option[Int], @@ -133,13 +175,11 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { val (fullName, signature) = typeInfoProvider.fullNameWithSignatureAsLambda(fn, name) val lambdaMethodNode = methodNode(fn, name, fullName, signature, relativizedPath) - case class NodeContext(node: NewNode, name: String, typeFullName: String) val closureBindingEntriesForCaptured = scope .pushClosureScope(lambdaMethodNode) .collect { case node: NewMethodParameterIn => NodeContext(node, node.name, node.typeFullName) case node: NewLocal => NodeContext(node, node.name, node.typeFullName) - case node: NewMember => NodeContext(node, node.name, node.typeFullName) } .map { capturedNodeContext => val uuidBytes = stringForUUID(fn, capturedNodeContext.name, capturedNodeContext.typeFullName) @@ -166,13 +206,14 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { astForParameter(p, idx) } val bodyAsts = Option(fn.getBodyBlockExpression) match { - case Some(bodyBlockExpression) => astsForBlock(bodyBlockExpression, None, None) + case Some(bodyBlockExpression) => + astsForBlock(bodyBlockExpression, None, None, localsForCaptures = localsForCaptured) case None => Option(fn.getBodyExpression) .map { expr => val bodyBlock = blockNode(expr, expr.getText, TypeConstants.any) val returnAst_ = returnAst(returnNode(expr, Constants.retCode), astsForExpression(expr, Some(1))) - Seq(blockAst(bodyBlock, List(returnAst_))) + Seq(blockAst(bodyBlock, localsForCaptured.map(Ast(_)) ++ List(returnAst_))) } .getOrElse { val bodyBlock = blockNode(fn, "", TypeConstants.any) @@ -189,7 +230,7 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { parametersAsts, bodyAst, newMethodReturnNode(returnTypeFullName, None, line(fn), column(fn)), - modifierNode(ModifierTypes.VIRTUAL) :: modifierNode(ModifierTypes.LAMBDA) :: Nil + NodeBuilders.newModifierNode(ModifierTypes.VIRTUAL) :: NodeBuilders.newModifierNode(ModifierTypes.LAMBDA) :: Nil ) val _methodRefNode = @@ -231,13 +272,11 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { val (fullName, signature) = typeInfoProvider.fullNameWithSignature(expr, name) val lambdaMethodNode = methodNode(expr, name, fullName, signature, relativizedPath) - case class NodeContext(node: NewNode, name: String, typeFullName: String) val closureBindingEntriesForCaptured = scope .pushClosureScope(lambdaMethodNode) .collect { case node: NewMethodParameterIn => NodeContext(node, node.name, node.typeFullName) case node: NewLocal => NodeContext(node, node.name, node.typeFullName) - case node: NewMember => NodeContext(node, node.name, node.typeFullName) } .map { capturedNodeContext => val uuidBytes = stringForUUID(expr, capturedNodeContext.name, capturedNodeContext.typeFullName) 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 68a92f5cca8b..edfa2fc16e54 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,13 +1,12 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants -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.joern.x2cpg.utils.NodeBuilders import io.shiftleft.codepropertygraph.generated.DispatchTypes import io.shiftleft.codepropertygraph.generated.Operators import io.shiftleft.codepropertygraph.generated.nodes.NewAnnotation @@ -59,7 +58,7 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { if (expr.hasInterpolation) { val args = expr.getEntries.filter(_.getExpression != null).zipWithIndex.map { case (entry, idx) => val entryTypeFullName = registerType(typeInfoProvider.expressionType(entry.getExpression, TypeConstants.any)) - val valueCallNode = operatorCallNode( + val valueCallNode = NodeBuilders.newOperatorCallNode( Operators.formattedValue, entry.getExpression.getText, Option(entryTypeFullName), @@ -70,7 +69,13 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { callAst(valueCallNode, valueArgs.toList) } val node = - operatorCallNode(Operators.formatString, expr.getText, Option(typeFullName), line(expr), column(expr)) + NodeBuilders.newOperatorCallNode( + Operators.formatString, + expr.getText, + Option(typeFullName), + line(expr), + column(expr) + ) callAst(withArgumentName(withArgumentIndex(node, argIdx), argName), args.toIndexedSeq.toList) } else { val node = literalNode(expr, expr.getText, typeFullName) @@ -107,7 +112,13 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { identifierNode(expr, expr.getIdentifier.getText, expr.getIdentifier.getText, typeFullName), fieldIdentifierNode(expr, Constants.companionObjectMemberName, Constants.companionObjectMemberName) ).map(Ast(_)) - val node = operatorCallNode(Operators.fieldAccess, expr.getText, Option(typeFullName), line(expr), column(expr)) + val node = NodeBuilders.newOperatorCallNode( + Operators.fieldAccess, + expr.getText, + Option(typeFullName), + line(expr), + column(expr) + ) callAst(withArgumentIndex(node, argIdx), argAsts) } else { val node = typeRefNode(expr.getIdentifier, expr.getIdentifier.getText, typeFullName) @@ -125,7 +136,7 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { val thisNode = identifierNode(expr, Constants.this_, Constants.this_, referenceTargetTypeFullName) val thisAst = astWithRefEdgeMaybe(Constants.this_, thisNode) val _fieldIdentifierNode = fieldIdentifierNode(expr, expr.getReferencedName, expr.getReferencedName) - val node = operatorCallNode( + val node = NodeBuilders.newOperatorCallNode( Operators.fieldAccess, s"${Constants.this_}.${expr.getReferencedName}", Option(typeFullName), @@ -227,14 +238,14 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { def astForPackageDeclaration(packageName: String): Ast = { val node = if (packageName == Constants.root) - namespaceBlockNode( + NodeBuilders.newNamespaceBlockNode( NamespaceTraversal.globalNamespaceName, NamespaceTraversal.globalNamespaceName, relativizedPath ) else { val name = packageName.split("\\.").lastOption.getOrElse("") - namespaceBlockNode(name, packageName, relativizedPath) + NodeBuilders.newNamespaceBlockNode(name, packageName, relativizedPath) } Ast(node) } 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 e4dbb2acb43a..b24d063a7543 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 @@ -1,11 +1,11 @@ package io.joern.kotlin2cpg.ast import io.joern.kotlin2cpg.Constants -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.ValidationMode +import io.joern.x2cpg.utils.NodeBuilders import io.joern.x2cpg.utils.NodeBuilders.newIdentifierNode import io.shiftleft.codepropertygraph.generated.ControlStructureTypes import io.shiftleft.codepropertygraph.generated.DispatchTypes @@ -85,7 +85,7 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { callAst(iteratorAssignmentRhs, Seq(), Option(Ast(iteratorAssignmentRhsIdentifier))) val iteratorAssignment = - operatorCallNode(Operators.assignment, s"$iteratorName = ${iteratorAssignmentRhs.code}", None) + NodeBuilders.newOperatorCallNode(Operators.assignment, s"$iteratorName = ${iteratorAssignmentRhs.code}", None) val iteratorAssignmentAst = callAst(iteratorAssignment, List(Ast(iteratorAssignmentLhs), iteratorAssignmentRhsAst)) val controlStructure = controlStructureNode(expr, ControlStructureTypes.WHILE, expr.getText) @@ -140,7 +140,8 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { val iteratorNextCallAst = callAst(iteratorNextCall, Seq(), Option(iteratorNextIdentifierAst)) - val tmpParameterNextAssignment = operatorCallNode(Operators.assignment, s"$tmpName = ${iteratorNextCall.code}") + val tmpParameterNextAssignment = + NodeBuilders.newOperatorCallNode(Operators.assignment, s"$tmpName = ${iteratorNextCall.code}") val tmpParameterNextAssignmentAst = callAst(tmpParameterNextAssignment, List(tmpIdentifierAst, iteratorNextCallAst)) val assignmentsForEntries = @@ -203,7 +204,7 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { val iteratorAssignmentRhsAst = callAst(iteratorAssignmentRhs, Seq(), Option(Ast(iteratorAssignmentRhsIdentifier))) val iteratorAssignment = - operatorCallNode(Operators.assignment, s"$iteratorName = ${iteratorAssignmentRhs.code}", None) + NodeBuilders.newOperatorCallNode(Operators.assignment, s"$iteratorName = ${iteratorAssignmentRhs.code}", None) val iteratorAssignmentAst = callAst(iteratorAssignment, List(Ast(iteratorAssignmentLhs), iteratorAssignmentRhsAst)) val controlStructure = controlStructureNode(expr, ControlStructureTypes.WHILE, expr.getText) @@ -249,7 +250,7 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { val iteratorNextCallAst = callAst(iteratorNextCall, Seq(), Option(iteratorNextIdentifierAst)) val loopParameterNextAssignment = - operatorCallNode(Operators.assignment, s"$loopParameterName = ${iteratorNextCall.code}", None) + NodeBuilders.newOperatorCallNode(Operators.assignment, s"$loopParameterName = ${iteratorNextCall.code}", None) val loopParameterNextAssignmentAst = callAst(loopParameterNextAssignment, List(Ast(loopParameterIdentifier), iteratorNextCallAst)) @@ -303,7 +304,13 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { if (allAsts.nonEmpty) { val returnTypeFullName = registerType(typeInfoProvider.expressionType(expr, TypeConstants.any)) val node = - operatorCallNode(Operators.conditional, expr.getText, Option(returnTypeFullName), line(expr), column(expr)) + NodeBuilders.newOperatorCallNode( + Operators.conditional, + expr.getText, + Option(returnTypeFullName), + line(expr), + column(expr) + ) callAst(withArgumentIndex(node, argIdx).argumentName(argNameMaybe), allAsts) .withChildren(annotations.map(astForAnnotationEntry)) } else { @@ -382,7 +389,7 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { ): Ast = { val callNode = - withArgumentIndex(operatorCallNode(".when", ".when", None), argIdx) + withArgumentIndex(NodeBuilders.newOperatorCallNode(".when", ".when", None), argIdx) .argumentName(argNameMaybe) val subjectExpressionAsts = Option(expr.getSubjectExpression) match { @@ -428,7 +435,13 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { val entryExprAst = astsForExpression(entryExpr, None).head val callNode = - operatorCallNode(Operators.conditional, Operators.conditional, Some(typeFullName), line(cond), column(cond)) + NodeBuilders.newOperatorCallNode( + Operators.conditional, + Operators.conditional, + Some(typeFullName), + line(cond), + column(cond) + ) val newElseAst = callAst(callNode, Seq(condAst, entryExprAst, elseAst)) elseAst = newElseAst @@ -506,7 +519,8 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { astsForExpression(entry.getCatchBody, None) } val node = - operatorCallNode(Operators.tryCatch, expr.getText, Option(typeFullName), line(expr), column(expr)) + NodeBuilders + .newOperatorCallNode(Operators.tryCatch, expr.getText, Option(typeFullName), line(expr), column(expr)) .argumentName(argNameMaybe) callAst(withArgumentIndex(node, argIdx), List(tryBlockAst) ++ clauseAsts) 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 deleted file mode 100644 index edd53027db1a..000000000000 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/Nodes.scala +++ /dev/null @@ -1,37 +0,0 @@ -package io.joern.kotlin2cpg.ast - -import io.shiftleft.codepropertygraph.generated.DispatchTypes -import io.shiftleft.codepropertygraph.generated.nodes.{NewCall, NewModifier, NewNamespaceBlock} - -object Nodes { - - def modifierNode(_type: String): NewModifier = { - NewModifier() - .modifierType(_type) - } - - def namespaceBlockNode(name: String, fullName: String, fileName: String): NewNamespaceBlock = { - NewNamespaceBlock() - .name(name) - .fullName(fullName) - .filename(fileName) - } - - def operatorCallNode( - name: String, - code: String, - typeFullName: Option[String] = None, - line: Option[Integer] = None, - column: Option[Integer] = None - ): NewCall = { - NewCall() - .name(name) - .methodFullName(name) - .code(code) - .signature("") - .dispatchType(DispatchTypes.STATIC_DISPATCH) - .typeFullName(typeFullName.getOrElse("ANY")) - .lineNumber(line) - .columnNumber(column) - } -} 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 d05dd08ebd38..08f434ff02c5 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 @@ -24,6 +24,8 @@ import org.jetbrains.kotlin.descriptors.impl.{ PropertyDescriptorImpl, TypeAliasConstructorDescriptorImpl } +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.load.java.`lazy`.descriptors.LazyJavaClassDescriptor import org.jetbrains.kotlin.load.java.sources.JavaSourceElement @@ -377,7 +379,9 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn desc = resolvedCallForSubexpression.getResultingDescriptor } yield desc - descMaybe.collect { case desc: FunctionDescriptor => desc } + descMaybe.collect { + case desc: FunctionDescriptor if desc.getKind == CallableMemberDescriptor.Kind.DECLARATION => desc + } } private def isConstructorDescriptor(desc: FunctionDescriptor): Boolean = { diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/dataflow/TypeDeclTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/dataflow/TypeDeclTests.scala index 6b27d97eef4a..d014e94e7631 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/dataflow/TypeDeclTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/dataflow/TypeDeclTests.scala @@ -129,6 +129,11 @@ class TypeDeclTests extends KotlinCode2CpgFixture(withOssDataflow = true) { |""".stripMargin) "have their own instance of primaryCtorCall nodes" in { + cpg.typeDecl.nameExact("AClass").method.isConstructor.fullName.l shouldBe List( + "AClass.:void()", + "AClass.:void(java.lang.String)", + "AClass.:void(java.lang.String,int)" + ) val List(call1, call2) = cpg.method.nameExact("").filter(_.parameter.size > 1).call.nameExact("").l call1.method.fullName shouldBe "AClass.:void(java.lang.String)" call2.method.fullName shouldBe "AClass.:void(java.lang.String,int)" diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/FileTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/FileTests.scala index 0c893a62794d..098c7f9329ba 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/FileTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/FileTests.scala @@ -42,7 +42,7 @@ class FileTests extends KotlinCode2CpgFixture(withOssDataflow = false) { } "should allow traversing from file to its type declarations" in { - cpg.file.nameNot(FileTraversal.UNKNOWN).typeDecl.name.toSet shouldBe Set("", "Foo") + cpg.file.nameNot(FileTraversal.UNKNOWN).typeDecl.name.sorted.toSet shouldBe Set("add", "baz", "Foo", "") } } } diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/LambdaTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/LambdaTests.scala index bf554eff687f..6de2838c017e 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/LambdaTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/LambdaTests.scala @@ -2,21 +2,22 @@ package io.joern.kotlin2cpg.querying import io.joern.kotlin2cpg.Constants import io.joern.kotlin2cpg.testfixtures.KotlinCode2CpgFixture -import io.shiftleft.codepropertygraph.generated.{DispatchTypes, EdgeTypes, EvaluationStrategies, ModifierTypes} -import io.shiftleft.codepropertygraph.generated.edges.{Capture, Ref} -import io.shiftleft.codepropertygraph.generated.nodes.{ - Binding, - Block, - Call, - ClosureBinding, - Local, - MethodRef, - Return, - TypeDecl -} +import io.joern.x2cpg.Defines +import io.shiftleft.codepropertygraph.generated.DispatchTypes +import io.shiftleft.codepropertygraph.generated.EdgeTypes +import io.shiftleft.codepropertygraph.generated.EvaluationStrategies +import io.shiftleft.codepropertygraph.generated.ModifierTypes +import io.shiftleft.codepropertygraph.generated.edges.Capture +import io.shiftleft.codepropertygraph.generated.nodes.Binding +import io.shiftleft.codepropertygraph.generated.nodes.Block +import io.shiftleft.codepropertygraph.generated.nodes.Call +import io.shiftleft.codepropertygraph.generated.nodes.ClosureBinding +import io.shiftleft.codepropertygraph.generated.nodes.Local +import io.shiftleft.codepropertygraph.generated.nodes.MethodRef +import io.shiftleft.codepropertygraph.generated.nodes.Return +import io.shiftleft.codepropertygraph.generated.nodes.TypeDecl import io.shiftleft.semanticcpg.language.* import overflowdb.traversal.jIteratortoTraversal -import io.joern.x2cpg.Defines class LambdaTests extends KotlinCode2CpgFixture(withOssDataflow = false, withDefaultJars = true) { "CPG for code with a simple lambda which captures a method parameter" should { @@ -545,8 +546,9 @@ class LambdaTests extends KotlinCode2CpgFixture(withOssDataflow = false, withDef } "contain a BINDING node for the lambda with the correct signature" in { - val List(b) = cpg.all.collect { case b: Binding => b }.l - b.signature shouldBe "java.lang.Object()" + val List(b1, b2) = cpg.typeDecl.methodBinding.l + b1.signature shouldBe "void(java.lang.String)" + b2.signature shouldBe "java.lang.Object()" } } diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/LocalFunctionsTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/LocalFunctionsTests.scala index 67da4e7701bd..bbb81317eeea 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/LocalFunctionsTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/LocalFunctionsTests.scala @@ -2,7 +2,8 @@ package io.joern.kotlin2cpg.querying import io.joern.kotlin2cpg.testfixtures.KotlinCode2CpgFixture import io.shiftleft.codepropertygraph.generated.nodes.Method -import io.shiftleft.semanticcpg.language._ +import io.shiftleft.codepropertygraph.generated.nodes.TypeDecl +import io.shiftleft.semanticcpg.language.* class LocalFunctionsTests extends KotlinCode2CpgFixture(withOssDataflow = false) { @@ -28,6 +29,17 @@ class LocalFunctionsTests extends KotlinCode2CpgFixture(withOssDataflow = false) m.block.size shouldBe 1 m.block.expressionDown.size shouldBe 1 } + + "contain TYPE_DECL nodes for the functions with the correct PROPS set" in { + val List(m1: Method) = cpg.method.nameExact("f1").l + val List(m2: Method) = cpg.method.nameExact("f2").l + val List(t1: TypeDecl) = cpg.typeDecl.internal.nameExact("f1").l + t1.fullName shouldBe "mypkg.f1:void(java.lang.String)" + t1.bindsOut.flatMap(_.refOut).l should contain(m1) + val List(t2: TypeDecl) = cpg.typeDecl.internal.nameExact("f2").l + t2.fullName shouldBe "mypkg.f1.f2:void(java.lang.String)" + t2.bindsOut.flatMap(_.refOut).l should contain(m2) + } } "CPG for code with deeply-nested local fn declarations" should { diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/ObjectExpressionTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/ObjectExpressionTests.scala index dbd7f1214b91..b2bfd2abca92 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/ObjectExpressionTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/ObjectExpressionTests.scala @@ -1,8 +1,12 @@ package io.joern.kotlin2cpg.querying import io.joern.kotlin2cpg.testfixtures.KotlinCode2CpgFixture -import io.shiftleft.codepropertygraph.generated.DispatchTypes -import io.shiftleft.codepropertygraph.generated.nodes.{Block, Call, Identifier, Local, Method, Return, TypeDecl} +import io.shiftleft.codepropertygraph.generated.nodes.Call +import io.shiftleft.codepropertygraph.generated.nodes.Identifier +import io.shiftleft.codepropertygraph.generated.nodes.Local +import io.shiftleft.codepropertygraph.generated.nodes.Method +import io.shiftleft.codepropertygraph.generated.nodes.Return +import io.shiftleft.codepropertygraph.generated.nodes.TypeDecl import io.shiftleft.semanticcpg.language.* class ObjectExpressionTests extends KotlinCode2CpgFixture(withOssDataflow = false) { @@ -30,10 +34,13 @@ class ObjectExpressionTests extends KotlinCode2CpgFixture(withOssDataflow = fals |} |""".stripMargin) - "should contain two TYPE_DECL nodes with the correct props set" in { - val List(firstTd: TypeDecl, secondTd: TypeDecl) = cpg.typeDecl.isExternal(false).nameNot("").l - firstTd.fullName shouldBe "mypkg.foo$object$1" - secondTd.fullName shouldBe "mypkg.foo$object$2" + "should contain TYPE_DECL nodes with the correct props set" in { + val List(p1, p2, foo, o1, o2) = cpg.typeDecl.isExternal(false).nameNot("").l + p1.fullName shouldBe "mypkg.foo$object$1.printWithSuffix:void(java.lang.String)" + p2.fullName shouldBe "mypkg.foo$object$2.printWithSuffix:void(java.lang.String)" + foo.fullName shouldBe "mypkg.foo:void(java.lang.String)" + o1.fullName shouldBe "mypkg.foo$object$1" + o2.fullName shouldBe "mypkg.foo$object$2" } "should contain two LOCAL nodes with the correct props set" in { @@ -81,12 +88,19 @@ class ObjectExpressionTests extends KotlinCode2CpgFixture(withOssDataflow = fals c.methodFullName shouldBe "mypkg.does:void(mypkg.AnInterface,java.lang.String)" } - "contain a TYPE_DECL node with the correct props set" in { - val List(_: TypeDecl, secondTd: TypeDecl) = cpg.typeDecl.isExternal(false).nameNot("").l - secondTd.name shouldBe "anonymous_obj" - secondTd.fullName shouldBe "mypkg.foo$object$1" - secondTd.inheritsFromTypeFullName shouldBe Seq("mypkg.AnInterface") - val List(firstMethod: Method, secondMethod: Method) = secondTd.boundMethod.l + "contain TYPE_DECL nodes with the correct props set" in { + val List(f1, does, f2, foo, interface, obj) = cpg.typeDecl.isExternal(false).nameNot("").l + f1.fullName shouldBe "mypkg.AnInterface.doSomething:void(java.lang.String)" + does.fullName shouldBe "mypkg.does:void(mypkg.AnInterface,java.lang.String)" + f2.fullName shouldBe "mypkg.foo$object$1.doSomething:void(java.lang.String)" + foo.fullName shouldBe "mypkg.foo:void(java.lang.String)" + interface.fullName shouldBe "mypkg.AnInterface" + interface.inheritsFromTypeFullName shouldBe List("java.lang.Object") + obj.name shouldBe "anonymous_obj" + obj.fullName shouldBe "mypkg.foo$object$1" + obj.inheritsFromTypeFullName shouldBe Seq("mypkg.AnInterface") + + val List(firstMethod: Method, secondMethod: Method) = obj.boundMethod.l firstMethod.fullName shouldBe "mypkg.foo$object$1.doSomething:void(java.lang.String)" secondMethod.fullName shouldBe "mypkg.foo$object$1.:void()" } @@ -123,12 +137,16 @@ class ObjectExpressionTests extends KotlinCode2CpgFixture(withOssDataflow = fals |} |""".stripMargin) - "contain a TYPE_DECL node with the correct props set" in { - val List(_: TypeDecl, _: TypeDecl, thirdTd: TypeDecl) = - cpg.typeDecl.isExternal(false).nameNot("").l - thirdTd.name shouldBe "anonymous_obj" - thirdTd.fullName shouldBe "mypkg.f1$object$1" - thirdTd.inheritsFromTypeFullName shouldBe Seq("mypkg.SomeInterface") + "contain TYPE_DECL nodes with the correct props set" in { + val List(interfaceF1, objectF1, f1, interface, qClass, obj) = cpg.typeDecl.isExternal(false).nameNot("").l + interfaceF1.fullName shouldBe "mypkg.SomeInterface.doSomething:void()" + objectF1.fullName shouldBe "mypkg.f1$object$1.doSomething:void()" + f1.fullName shouldBe "mypkg.f1:void()" + interface.fullName shouldBe "mypkg.SomeInterface" + qClass.fullName shouldBe "mypkg.QClass" + obj.name shouldBe "anonymous_obj" + obj.fullName shouldBe "mypkg.f1$object$1" + obj.inheritsFromTypeFullName shouldBe Seq("mypkg.SomeInterface") } } diff --git a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/TypeDeclTests.scala b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/TypeDeclTests.scala index 986266033317..e05da10d7311 100644 --- a/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/TypeDeclTests.scala +++ b/joern-cli/frontends/kotlin2cpg/src/test/scala/io/joern/kotlin2cpg/querying/TypeDeclTests.scala @@ -310,8 +310,9 @@ class TypeDeclTests extends KotlinCode2CpgFixture(withOssDataflow = false) { |""".stripMargin) "should contain a BINDING node for X with the correct props set" in { - val List(b) = cpg.all.collect { case b: Binding => b }.filter { b => b.name == "add1" }.l + val List(b) = cpg.typeDecl.methodBinding.nameExact("add1").l b.name shouldBe "add1" + b.methodFullName shouldBe "mypkg.Foo.add1:int(int)" b.signature shouldBe "int(int)" } } diff --git a/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/utils/NodeBuilders.scala b/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/utils/NodeBuilders.scala index f05e28ca70f4..38dd07f80326 100644 --- a/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/utils/NodeBuilders.scala +++ b/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/utils/NodeBuilders.scala @@ -15,6 +15,7 @@ import io.shiftleft.codepropertygraph.generated.nodes.{ NewModifier } import io.shiftleft.codepropertygraph.generated.{DispatchTypes, EvaluationStrategies} +import io.shiftleft.codepropertygraph.generated.nodes.NewNamespaceBlock /** NodeBuilders helps with node creation and is intended to be used when functions from `x2cpg.AstCreatorBase` are not * appropriate; for example, in cases in which the node's line and column are _not_ set from the base ASTNode type of a @@ -177,4 +178,11 @@ object NodeBuilders { .evaluationStrategy(EvaluationStrategies.BY_VALUE) .lineNumber(line) .columnNumber(column) + + def newNamespaceBlockNode(name: String, fullName: String, fileName: String): NewNamespaceBlock = { + NewNamespaceBlock() + .name(name) + .fullName(fullName) + .filename(fileName) + } }