Skip to content

Commit

Permalink
[c#] Ast for foreach statements (joernio#4072)
Browse files Browse the repository at this point in the history
This PR includes,

1. Upgrading to new `DotNetAstGen` version.
2. AST Creation for `foreach` statements.
3. Tests for `foreach` statements.
4. Type Mapping for `PredefinedType` node.

Resolves joernio#3986
  • Loading branch information
karan-batavia authored Jan 19, 2024
1 parent cc1db59 commit 52a1016
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
csharpsrc2cpg {
dotnetastgen_version: "0.14.0"
dotnetastgen_version: "0.15.0"
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
case IdentifierName =>
// TODO: Look at scope object for possible types
Defines.Any
case PredefinedType =>
BuiltinTypes.DotNetTypeMap.getOrElse(node.code, Defines.Any)
case _ =>
Try(createDotNetNodeInfo(node.json(ParserKeys.Type))) match
case Success(typeNode) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import io.circe.Json
import io.joern.csharpsrc2cpg.parser.DotNetJsonAst.{
BinaryExpr,
Block,
ExpressionStatement,
ForEachStatement,
GlobalStatement,
IfStatement,
LiteralExpr,
ThrowStatement,
TryStatement,
UnaryExpr
}
import io.joern.csharpsrc2cpg.parser.DotNetJsonAst.{ExpressionStatement, GlobalStatement, ThrowStatement, TryStatement}
import io.circe.Json
import io.joern.csharpsrc2cpg.parser.DotNetJsonAst.{
BinaryExpr,
Block,
Expand Down Expand Up @@ -54,9 +56,29 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { t
case IfStatement => astForIfStatement(nodeInfo)
case ThrowStatement => astForThrowStatement(nodeInfo)
case TryStatement => astForTryStatement(nodeInfo)
case ForEachStatement => astForForEachStatement(nodeInfo)
case _ => notHandledYet(nodeInfo)
}

private def astForForEachStatement(forEachStmt: DotNetNodeInfo): Seq[Ast] = {
val forEachNode = controlStructureNode(forEachStmt, ControlStructureTypes.FOR, forEachStmt.code)
val iterableAst = astForNode(forEachStmt.json(ParserKeys.Expression))
val forEachBlockAst = astForBlock(createDotNetNodeInfo(forEachStmt.json(ParserKeys.Statement)))

val identifierValue = forEachStmt.json(ParserKeys.Identifier)(ParserKeys.Value).str
val _identifierNode =
identifierNode(
node = createDotNetNodeInfo(forEachStmt.json(ParserKeys.Type)),
name = identifierValue,
code = identifierValue,
typeFullName = nodeTypeFullName(createDotNetNodeInfo(forEachStmt.json(ParserKeys.Type)))
)

val iteratorVarAst = Ast(_identifierNode)

Seq(Ast(forEachNode).withChild(iteratorVarAst).withChildren(iterableAst).withChild(forEachBlockAst))
}

private def astForElseStatement(elseParserNode: DotNetNodeInfo): Ast = {
val elseNode = controlStructureNode(elseParserNode, ControlStructureTypes.ELSE, "else")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ object DotNetJsonAst {

object FinallyClause extends ClauseExpr

object ForEachStatement extends BaseStmt

}

/** The JSON key values, in alphabetical order.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.joern.csharpsrc2cpg.querying.ast

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

class LoopsTests extends CSharpCode2CpgFixture {
"AST Creation for loops" should {
"be correct for foreach statement" in {
val cpg = code(basicBoilerplate("""
|List<int> fibNumbers = [0, 1, 1, 2, 3, 5, 8, 13];
|foreach (int element in fibNumbers)
|{
| Console.Write($"{element} ");
|}
|""".stripMargin))

inside(cpg.method("Main").controlStructure.l) {
case forEachNode :: Nil =>
forEachNode.controlStructureType shouldBe ControlStructureTypes.FOR

inside(forEachNode.astChildren.isIdentifier.l) {
case iteratorNode :: iterableNode :: Nil =>
iteratorNode.code shouldBe "element"
iteratorNode.typeFullName shouldBe "System.Int32"

iterableNode.code shouldBe "fibNumbers"
iterableNode.typeFullName shouldBe "List<int>"
case _ => fail("No node for iterable found in `foreach` statement")
}

inside(forEachNode.astChildren.isBlock.l) {
case blockNode :: Nil =>
val List(writeCall) = cpg.call.nameExact("Write").l
writeCall.astParent shouldBe blockNode
case _ => fail("Correct blockNode as child not found for `foreach` statement")
}

case _ => fail("No control structure node found for `foreach`.")
}
}
}
}

0 comments on commit 52a1016

Please sign in to comment.