diff --git a/joern-cli/frontends/csharpsrc2cpg/src/main/resources/application.conf b/joern-cli/frontends/csharpsrc2cpg/src/main/resources/application.conf index 4bf5c1e12209..fdb034dcc328 100644 --- a/joern-cli/frontends/csharpsrc2cpg/src/main/resources/application.conf +++ b/joern-cli/frontends/csharpsrc2cpg/src/main/resources/application.conf @@ -1,3 +1,3 @@ csharpsrc2cpg { - dotnetastgen_version: "0.14.0" + dotnetastgen_version: "0.15.0" } diff --git a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstCreatorHelper.scala b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstCreatorHelper.scala index 27028f14f091..4ba0c22db5e0 100644 --- a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstCreatorHelper.scala +++ b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstCreatorHelper.scala @@ -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) => diff --git a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstForStatementsCreator.scala b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstForStatementsCreator.scala index ed193da865d3..e7ac362f91ef 100644 --- a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstForStatementsCreator.scala +++ b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstForStatementsCreator.scala @@ -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, @@ -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") diff --git a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/parser/DotNetJsonAst.scala b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/parser/DotNetJsonAst.scala index 846445b1199a..c1bb1d7eeeb0 100644 --- a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/parser/DotNetJsonAst.scala +++ b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/parser/DotNetJsonAst.scala @@ -146,6 +146,8 @@ object DotNetJsonAst { object FinallyClause extends ClauseExpr + object ForEachStatement extends BaseStmt + } /** The JSON key values, in alphabetical order. diff --git a/joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/LoopsTests.scala b/joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/LoopsTests.scala new file mode 100644 index 000000000000..53c24da8d9f6 --- /dev/null +++ b/joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/LoopsTests.scala @@ -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 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" + 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`.") + } + } + } +}