Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[c#] create implicit ctor for static field inits #5209

Merged
merged 1 commit into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

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