Skip to content

Commit

Permalink
[kotlin2cpg] Secondary Constructor Sharing Same <init> (joernio#3743)
Browse files Browse the repository at this point in the history
Fixed a bug where secondary constructors share the same call node to the primary constructor.

Resolves joernio#3706
  • Loading branch information
DavidBakerEffendi authored Oct 17, 2023
1 parent eb0440a commit 407b32f
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,8 @@ import io.joern.x2cpg.utils.NodeBuilders.{newBindingNode, newIdentifierNode, new
import io.joern.x2cpg.{Ast, Defines, ValidationMode}
import io.shiftleft.codepropertygraph.generated.nodes.{NewBlock, NewCall, NewMethod, NewTypeDecl}
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, EdgeTypes, Operators}
import org.jetbrains.kotlin.psi.{
KtAnnotationEntry,
KtArrayAccessExpression,
KtBlockExpression,
KtCallExpression,
KtClass,
KtClassOrObject,
KtDeclaration,
KtDestructuringDeclaration,
KtDotQualifiedExpression,
KtExpression,
KtIfExpression,
KtNameReferenceExpression,
KtNamedFunction,
KtObjectLiteralExpression,
KtParameter,
KtPostfixExpression,
KtProperty,
KtPsiUtil,
KtQualifiedExpression,
KtSecondaryConstructor,
KtWhenExpression
}
import io.shiftleft.semanticcpg.language.*
import org.jetbrains.kotlin.psi.*

import scala.jdk.CollectionConverters.*
import scala.util.Random
Expand Down Expand Up @@ -417,6 +395,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) {
implicit typeInfoProvider: TypeInfoProvider
): Seq[Ast] = {
ctors.map { ctor =>
val primaryCtorCallAst = List(Ast(primaryCtorCall.copy))
val constructorParams = ctor.getValueParameters.asScala.toList
val defaultSignature = typeInfoProvider.anySignature(constructorParams)
val defaultFullName = s"$classFullName.${TypeConstants.initPrefix}:$defaultSignature"
Expand All @@ -435,10 +414,10 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) {
val ctorMethodBlockAsts =
ctor.getBodyExpression match {
case b: KtBlockExpression =>
astsForBlock(b, None, None, preStatements = Option(Seq(Ast(primaryCtorCall))))
astsForBlock(b, None, None, preStatements = Option(primaryCtorCallAst))
case null =>
val node = NewBlock().code(Constants.empty).typeFullName(TypeConstants.any)
Seq(blockAst(node, List(Ast(primaryCtorCall))))
Seq(blockAst(node, primaryCtorCallAst))
}
scope.popScope()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package io.joern.kotlin2cpg.dataflow

import io.joern.dataflowengineoss.language.toExtendedCfgNode
import io.joern.kotlin2cpg.testfixtures.KotlinCode2CpgFixture
import io.shiftleft.semanticcpg.language._
import io.shiftleft.semanticcpg.language.*

class TypeDeclTests extends KotlinCode2CpgFixture(withOssDataflow = true) {
implicit val resolver: ICallResolver = NoResolve
Expand Down Expand Up @@ -115,4 +115,24 @@ class TypeDeclTests extends KotlinCode2CpgFixture(withOssDataflow = true) {
}
}

"CPG for classes with secondary constructors" should {
val cpg = code("""
|class AClass {
| var x: String
| constructor(q: String) {
| this.x = q
| }
| constructor(p: String, r: Int) {
| this.x = p
| }
|}
|""".stripMargin)

"have their own instance of primaryCtorCall nodes" in {
val List(call1, call2) = cpg.method.nameExact("<init>").filter(_.parameter.size > 1).call.nameExact("<init>").l
call1.method.fullName shouldBe "AClass.<init>:void(java.lang.String)"
call2.method.fullName shouldBe "AClass.<init>:void(java.lang.String,int)"
}
}

}

0 comments on commit 407b32f

Please sign in to comment.