Skip to content

Commit 457d8d8

Browse files
authored
[c#] support top-level method declarations (#5224)
1 parent a922084 commit 457d8d8

File tree

4 files changed

+32
-5
lines changed

4 files changed

+32
-5
lines changed

joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstForDeclarationsCreator.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,10 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) {
410410
Seq(methodAst(methodNode_, thisNode +: params, body, methodReturn, modifiers))
411411
}
412412

413-
protected def astForMethodDeclaration(methodDecl: DotNetNodeInfo): Seq[Ast] = {
413+
protected def astForMethodDeclaration(
414+
methodDecl: DotNetNodeInfo,
415+
extraModifiers: List[NewModifier] = Nil
416+
): Seq[Ast] = {
414417
val name = nameFromNode(methodDecl)
415418
val params = methodDecl
416419
.json(ParserKeys.ParameterList)
@@ -440,7 +443,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) {
440443
if (!jsonBody.isNull && parseLevel == AstParseLevel.FULL_AST) astForBlock(createDotNetNodeInfo(jsonBody))
441444
else Ast(blockNode(methodDecl)) // Creates an empty block
442445
scope.popScope()
443-
val modifiers = astForModifiers(methodDecl).flatMap(_.nodes).collect { case x: NewModifier => x }
446+
val modifiers = astForModifiers(methodDecl).flatMap(_.nodes).collect { case x: NewModifier => x } ++ extraModifiers
444447
val thisNode =
445448
if (!modifiers.exists(_.modifierType == ModifierTypes.STATIC)) astForThisParameter(methodDecl)
446449
else Ast()

joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstForStatementsCreator.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import io.joern.csharpsrc2cpg.parser.ParserKeys
66
import io.joern.csharpsrc2cpg.parser.DotNetJsonAst.*
77
import io.joern.x2cpg.Ast
88
import io.joern.x2cpg.ValidationMode
9-
import io.shiftleft.codepropertygraph.generated.{ControlStructureTypes, DispatchTypes, Operators}
9+
import io.joern.x2cpg.utils.NodeBuilders.newModifierNode
10+
import io.shiftleft.codepropertygraph.generated.{ControlStructureTypes, DispatchTypes, ModifierTypes, Operators}
1011
import io.shiftleft.codepropertygraph.generated.nodes.{
1112
NewControlStructure,
1213
NewFieldIdentifier,
@@ -279,8 +280,14 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { t
279280

280281
}
281282

282-
protected def astForGlobalStatement(globalStatement: DotNetNodeInfo): Seq[Ast] = {
283-
astForNode(globalStatement.json(ParserKeys.Statement))
283+
private def astForGlobalStatement(globalStatement: DotNetNodeInfo): Seq[Ast] = {
284+
val stmtNodeInfo = createDotNetNodeInfo(globalStatement.json(ParserKeys.Statement))
285+
stmtNodeInfo.node match
286+
// Denotes a top-level method declaration. These shall be added to the fictitious "main" created
287+
// by `astForTopLevelStatements`.
288+
case LocalFunctionStatement =>
289+
astForMethodDeclaration(stmtNodeInfo, extraModifiers = newModifierNode(ModifierTypes.STATIC) :: Nil)
290+
case _ => astForNode(stmtNodeInfo)
284291
}
285292

286293
private def astForJumpStatement(jumpStmt: DotNetNodeInfo): Seq[Ast] = {

joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/parser/DotNetJsonAst.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ object DotNetJsonAst {
226226

227227
object ReturnStatement extends JumpStatement
228228

229+
object LocalFunctionStatement extends DeclarationExpr with BaseStmt
230+
229231
object AwaitExpression extends BaseExpr
230232

231233
object PropertyDeclaration extends DeclarationExpr

joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/TopLevelStatementTests.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.joern.csharpsrc2cpg.querying.ast
22

33
import io.joern.csharpsrc2cpg.testfixtures.CSharpCode2CpgFixture
4+
import io.shiftleft.codepropertygraph.generated.ModifierTypes
45
import io.shiftleft.semanticcpg.language.*
56

67
class TopLevelStatementTests extends CSharpCode2CpgFixture {
@@ -60,4 +61,18 @@ class TopLevelStatementTests extends CSharpCode2CpgFixture {
6061
}
6162
}
6263

64+
"top-level method becomes an inner static local method to the fictitious main" in {
65+
val cpg = code("""
66+
|void Run() {}
67+
|""".stripMargin)
68+
inside(cpg.method.nameExact("Run").l) {
69+
case run :: Nil =>
70+
run.methodReturn.typeFullName shouldBe "void"
71+
run.fullName shouldBe "Test0_cs_Program.<Main>$.Run:void()"
72+
run.modifier.modifierType.toSet shouldBe Set(ModifierTypes.STATIC, ModifierTypes.INTERNAL)
73+
run.parentBlock.method.l shouldBe cpg.method.fullNameExact("Test0_cs_Program.<Main>$").l
74+
case xs =>
75+
fail(s"Expected single METHOD named Run, but found $xs")
76+
}
77+
}
6378
}

0 commit comments

Comments
 (0)