Skip to content

Commit

Permalink
[c#] support top-level method declarations (#5224)
Browse files Browse the repository at this point in the history
  • Loading branch information
xavierpinho authored Jan 14, 2025
1 parent a922084 commit 457d8d8
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,10 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) {
Seq(methodAst(methodNode_, thisNode +: params, body, methodReturn, modifiers))
}

protected def astForMethodDeclaration(methodDecl: DotNetNodeInfo): Seq[Ast] = {
protected def astForMethodDeclaration(
methodDecl: DotNetNodeInfo,
extraModifiers: List[NewModifier] = Nil
): Seq[Ast] = {
val name = nameFromNode(methodDecl)
val params = methodDecl
.json(ParserKeys.ParameterList)
Expand Down Expand Up @@ -440,7 +443,7 @@ trait AstForDeclarationsCreator(implicit withSchemaValidation: ValidationMode) {
if (!jsonBody.isNull && parseLevel == AstParseLevel.FULL_AST) astForBlock(createDotNetNodeInfo(jsonBody))
else Ast(blockNode(methodDecl)) // Creates an empty block
scope.popScope()
val modifiers = astForModifiers(methodDecl).flatMap(_.nodes).collect { case x: NewModifier => x }
val modifiers = astForModifiers(methodDecl).flatMap(_.nodes).collect { case x: NewModifier => x } ++ extraModifiers
val thisNode =
if (!modifiers.exists(_.modifierType == ModifierTypes.STATIC)) astForThisParameter(methodDecl)
else Ast()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import io.joern.csharpsrc2cpg.parser.ParserKeys
import io.joern.csharpsrc2cpg.parser.DotNetJsonAst.*
import io.joern.x2cpg.Ast
import io.joern.x2cpg.ValidationMode
import io.shiftleft.codepropertygraph.generated.{ControlStructureTypes, DispatchTypes, Operators}
import io.joern.x2cpg.utils.NodeBuilders.newModifierNode
import io.shiftleft.codepropertygraph.generated.{ControlStructureTypes, DispatchTypes, ModifierTypes, Operators}
import io.shiftleft.codepropertygraph.generated.nodes.{
NewControlStructure,
NewFieldIdentifier,
Expand Down Expand Up @@ -279,8 +280,14 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { t

}

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

private def astForJumpStatement(jumpStmt: DotNetNodeInfo): Seq[Ast] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ object DotNetJsonAst {

object ReturnStatement extends JumpStatement

object LocalFunctionStatement extends DeclarationExpr with BaseStmt

object AwaitExpression extends BaseExpr

object PropertyDeclaration extends DeclarationExpr
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.joern.csharpsrc2cpg.querying.ast

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

class TopLevelStatementTests extends CSharpCode2CpgFixture {
Expand Down Expand Up @@ -60,4 +61,18 @@ class TopLevelStatementTests extends CSharpCode2CpgFixture {
}
}

"top-level method becomes an inner static local method to the fictitious main" in {
val cpg = code("""
|void Run() {}
|""".stripMargin)
inside(cpg.method.nameExact("Run").l) {
case run :: Nil =>
run.methodReturn.typeFullName shouldBe "void"
run.fullName shouldBe "Test0_cs_Program.<Main>$.Run:void()"
run.modifier.modifierType.toSet shouldBe Set(ModifierTypes.STATIC, ModifierTypes.INTERNAL)
run.parentBlock.method.l shouldBe cpg.method.fullNameExact("Test0_cs_Program.<Main>$").l
case xs =>
fail(s"Expected single METHOD named Run, but found $xs")
}
}
}

0 comments on commit 457d8d8

Please sign in to comment.