Skip to content

Commit

Permalink
[c#] create implicit ctor for static field inits
Browse files Browse the repository at this point in the history
  • Loading branch information
xavierpinho committed Jan 7, 2025
1 parent 937c2ff commit 6007952
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) {
val modifiers = astForModifiers(classDecl)
val members = astForMembers(classDecl.json(ParserKeys.Members).arr.map(createDotNetNodeInfo).toSeq)
++ addConstructorWithFieldInitializationsIfNeeded(fullName)
// TODO: do the same for static fields
++ addStaticConstructorWithFieldInitializationsIfNeeded(fullName)

scope.popScope()
val typeDeclAst = Ast(typeDecl)
Expand All @@ -93,7 +93,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) {

if (shouldBuildCtor) {
val methodReturn = newMethodReturnNode(BuiltinTypes.Void, None, None, None)
val signature = composeMethodLikeSignature(BuiltinTypes.Void, Seq.empty)
val signature = composeMethodLikeSignature(methodReturn.typeFullName, Seq.empty)
val modifiers = Seq(newModifierNode(ModifierTypes.CONSTRUCTOR), newModifierNode(ModifierTypes.INTERNAL))
val name = Defines.ConstructorMethodName
val fullName = composeMethodFullName(typeDeclFullName, name, signature)
Expand Down Expand Up @@ -127,6 +127,42 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) {
}
}

private def addStaticConstructorWithFieldInitializationsIfNeeded(typeDeclFullname: String): Seq[Ast] = {
val staticFields = scope.getFieldsInScope.filter(f => f.isStatic && f.isInitialized)
val hasExplicitCtor =
scope.tryResolveTypeReference(typeDeclFullname).exists(_.methods.exists(_.name == Defines.StaticInitMethodName))
val shouldBuildCtor = staticFields.nonEmpty && !hasExplicitCtor && parseLevel == FULL_AST

if (shouldBuildCtor) {
val methodReturn = newMethodReturnNode(BuiltinTypes.Void, None, None, None)
val signature = composeMethodLikeSignature(methodReturn.typeFullName, Nil)
val modifiers = Seq(
newModifierNode(ModifierTypes.CONSTRUCTOR),
newModifierNode(ModifierTypes.INTERNAL),
newModifierNode(ModifierTypes.STATIC)
)
val name = Defines.StaticInitMethodName
val fullName = composeMethodFullName(typeDeclFullname, name, signature)

val body = {
scope.pushNewScope(MethodScope(fullName))
val fieldInitAssignmentAsts = astVariableDeclarationForInitializedFields(staticFields)
scope.popScope()
Ast(NewBlock().typeFullName(Defines.Any)).withChildren(fieldInitAssignmentAsts)
}

val methodNode_ = NewMethod()
.name(name)
.fullName(fullName)
.signature(signature)
.filename(relativeFileName)

methodAst(methodNode_, Nil, body, methodReturn, modifiers) :: Nil
} else {
Nil
}
}

protected def astForRecordDeclaration(recordDecl: DotNetNodeInfo): Seq[Ast] = {
val name = nameFromNode(recordDecl)
val fullName = astFullName(recordDecl)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,25 @@ class MemberTests extends CSharpCode2CpgFixture {
}
}

// TODO: Not supported yet.
"have a static constructor" ignore {
"have a static constructor" in {
inside(cpg.typeDecl.nameExact("Car").method.nameExact(Defines.StaticInitMethodName).l) {
case cctor :: Nil =>
cctor.fullName shouldBe s"Car.${Defines.StaticInitMethodName}:void()"
cctor.modifier.modifierType.toSet shouldBe Set(ModifierTypes.STATIC, ModifierTypes.CONSTRUCTOR)
cctor.modifier.modifierType.toSet shouldBe Set(
ModifierTypes.STATIC,
ModifierTypes.CONSTRUCTOR,
ModifierTypes.INTERNAL
)
cctor.methodReturn.typeFullName shouldBe "void"
case xs =>
fail(s"Expected single static constructor, but got $xs")
}
}

// TODO: Not supported yet.
"have the static member initialization inside the static constructor" ignore {
"have the static member initialization inside the static constructor" in {
inside(cpg.method.fullNameExact(s"Car.${Defines.StaticInitMethodName}:void()").body.assignment.l) {
case assignment :: Nil =>
assignment.target.code shouldBe "Car.nonInitMaxSpeed"
assignment.target.code shouldBe "nonInitMaxSpeed"
assignment.source.code shouldBe "200"
case xs =>
fail(s"Expected single assignment inside the static constructor, but got $xs")
Expand Down

0 comments on commit 6007952

Please sign in to comment.