Skip to content

Commit

Permalink
[c#] lower setter declaration into a METHOD node (#5271)
Browse files Browse the repository at this point in the history
  • Loading branch information
xavierpinho authored Jan 29, 2025
1 parent d12e5d3 commit 720a470
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import io.joern.csharpsrc2cpg.astcreation.BuiltinTypes.DotNetTypeMap
import io.joern.csharpsrc2cpg.datastructures.*
import io.joern.csharpsrc2cpg.parser.DotNetJsonAst.*
import io.joern.csharpsrc2cpg.parser.{DotNetNodeInfo, ParserKeys}
import io.joern.csharpsrc2cpg.utils.Utils.{composeGetterName, composeMethodFullName, composeMethodLikeSignature}
import io.joern.csharpsrc2cpg.utils.Utils.{
composeGetterName,
composeMethodFullName,
composeMethodLikeSignature,
composeSetterName
}
import io.joern.x2cpg.utils.NodeBuilders.{newMethodReturnNode, newModifierNode}
import io.joern.x2cpg.{Ast, Defines, ValidationMode}
import io.shiftleft.codepropertygraph.generated.*
Expand Down Expand Up @@ -547,11 +552,30 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) {
private def astForPropertyAccessor(accessorDecl: DotNetNodeInfo, propertyDecl: DotNetNodeInfo): Seq[Ast] = {
accessorDecl.node match
case GetAccessorDeclaration => astForGetAccessorDeclaration(accessorDecl, propertyDecl)
case SetAccessorDeclaration => astForSetAccessorDeclaration(accessorDecl, propertyDecl)
case _ =>
logger.warn(s"Unhandled property accessor '${accessorDecl.node}''")
logger.warn(s"Unhandled property accessor '${accessorDecl.node}'")
Nil
}

private def astForSetAccessorDeclaration(accessorDecl: DotNetNodeInfo, propertyDecl: DotNetNodeInfo): Seq[Ast] = {
val name = composeSetterName(nameFromNode(propertyDecl))
val modifiers = modifiersForNode(propertyDecl)
val returnType = BuiltinTypes.Void
val valueType = nodeTypeFullName(propertyDecl)
val baseType = scope.surroundingTypeDeclFullName.getOrElse(Defines.UnresolvedNamespace)
val isStatic = modifiers.exists(_.modifierType == ModifierTypes.STATIC)
val valueParam = Ast(NewMethodParameterIn().typeFullName(valueType).name("value").index(1))
val parameters = Option.unless(isStatic)(astForThisParameter(propertyDecl)).toList :+ valueParam
val signature = composeMethodLikeSignature(returnType, parameters)
val fullName = composeMethodFullName(baseType, name, signature)
val body = Try(astForBlock(createDotNetNodeInfo(accessorDecl.json(ParserKeys.Body)))).getOrElse(Ast())
val methodReturn = methodReturnNode(accessorDecl, returnType)
val methodNode_ = methodNode(accessorDecl, name, fullName, signature, relativeFileName)

methodAst(methodNode_, parameters, body, methodReturn, modifiers) :: Nil
}

private def astForGetAccessorDeclaration(accessorDecl: DotNetNodeInfo, propertyDecl: DotNetNodeInfo): Seq[Ast] = {
val name = composeGetterName(nameFromNode(propertyDecl))
val modifiers = modifiersForNode(propertyDecl)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ object Utils {

def composeGetterName(fieldIdentifierName: String): String = s"get_$fieldIdentifierName"

def composeSetterName(fieldIdentifierName: String): String = s"set_$fieldIdentifierName"

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package io.joern.csharpsrc2cpg.querying.ast

import io.joern.csharpsrc2cpg.testfixtures.CSharpCode2CpgFixture
import io.shiftleft.codepropertygraph.generated.ModifierTypes
import io.shiftleft.codepropertygraph.generated.nodes.Call
import io.shiftleft.semanticcpg.language.*

class PropertySetterTests extends CSharpCode2CpgFixture {

"uninitialized set-only property declaration" should {
val cpg = code("""
|using System;
|class C
|{
| public int MyProperty { set { Console.WriteLine(value); } }
|}
|""".stripMargin)

"be lowered into a set_* method" in {
inside(cpg.method.nameExact("set_MyProperty").l) {
case method :: Nil =>
method.fullName shouldBe "C.set_MyProperty:void(C,System.Int32)"
method.signature shouldBe "void(C,System.Int32)"
case xs => fail(s"Expected single set_MyProperty method, but got $xs")
}
}

"have correct modifiers" in {
cpg.method.nameExact("set_MyProperty").modifier.modifierType.sorted.l shouldBe List(ModifierTypes.PUBLIC)
}

"have correct parameters" in {
inside(cpg.method.nameExact("set_MyProperty").parameter.sortBy(_.index).l) {
case thisArg :: valueArg :: Nil =>
thisArg.index shouldBe 0
thisArg.name shouldBe "this"
thisArg.typeFullName shouldBe "C"

valueArg.index shouldBe 1
valueArg.name shouldBe "value"
valueArg.typeFullName shouldBe "System.Int32"
case xs => fail(s"Expected two arguments to set_MyProperty, but got $xs")
}
}

"have correct body" in {
inside(cpg.method.nameExact("set_MyProperty").body.flatMap(_.astChildren).l) {
case (writeLine: Call) :: Nil =>
writeLine.code shouldBe "Console.WriteLine(value)"
writeLine.methodFullName shouldBe "System.Console.WriteLine:System.Void(System.Boolean)"
case xs => fail(s"Expected single node inside set_MyProperty's body, but got $xs")
}
}
}

"uninitialized static set-only property declaration" should {
val cpg = code("""
|class C
|{
| public static int MyProperty { set { } }
|}
|""".stripMargin)

"be lowered into a set_* method" in {
inside(cpg.method.nameExact("set_MyProperty").l) {
case method :: Nil =>
method.fullName shouldBe "C.set_MyProperty:void(System.Int32)"
method.signature shouldBe "void(System.Int32)"
case xs => fail(s"Expected single set_MyProperty method, but got $xs")
}
}

"have correct modifiers" in {
cpg.method.nameExact("set_MyProperty").modifier.modifierType.sorted.l shouldBe List(
ModifierTypes.PUBLIC,
ModifierTypes.STATIC
)
}

"have correct parameters" in {
inside(cpg.method.nameExact("set_MyProperty").parameter.sortBy(_.index).l) {
case valueArg :: Nil =>
valueArg.index shouldBe 1
valueArg.name shouldBe "value"
valueArg.typeFullName shouldBe "System.Int32"
case xs => fail(s"Expected two arguments to set_MyProperty, but got $xs")
}
}

"have correct body" in {
cpg.method.nameExact("set_MyProperty").body.flatMap(_.astChildren) shouldBe empty
}
}

}

0 comments on commit 720a470

Please sign in to comment.