diff --git a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstSummaryVisitor.scala b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstSummaryVisitor.scala index 9915b71c51d9..7c316acb3e38 100644 --- a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstSummaryVisitor.scala +++ b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/astcreation/AstSummaryVisitor.scala @@ -58,6 +58,8 @@ trait AstSummaryVisitor(implicit withSchemaValidation: ValidationMode) { this: A def imports = cpg.imports.importedEntity.toSet + def globalImports = cpg.imports.filter(_.code.startsWith("global")).importedEntity.toSet + def toMethod(m: Method): CSharpMethod = { CSharpMethod( m.name, @@ -78,7 +80,7 @@ trait AstSummaryVisitor(implicit withSchemaValidation: ValidationMode) { this: A }) }) .asInstanceOf[NamespaceToTypeMap] - CSharpProgramSummary(mapping, imports) + CSharpProgramSummary(mapping, imports, globalImports) } } diff --git a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/datastructures/CSharpProgramSummary.scala b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/datastructures/CSharpProgramSummary.scala index ad56a16af386..2b4d389b2229 100644 --- a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/datastructures/CSharpProgramSummary.scala +++ b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/datastructures/CSharpProgramSummary.scala @@ -10,7 +10,6 @@ import upickle.default.* import java.io.{ByteArrayInputStream, InputStream} import scala.annotation.targetName -import scala.collection.mutable.ListBuffer import scala.io.Source import scala.util.{Failure, Success, Try} import java.net.JarURLConnection @@ -27,14 +26,18 @@ type NamespaceToTypeMap = mutable.Map[String, mutable.Set[CSharpType]] * @see * [[CSharpProgramSummary.jsonToInitialMapping]] for generating initial mappings. */ -case class CSharpProgramSummary(namespaceToType: NamespaceToTypeMap, imports: Set[String]) +case class CSharpProgramSummary(namespaceToType: NamespaceToTypeMap, imports: Set[String], globalImports: Set[String]) extends ProgramSummary[CSharpType, CSharpMethod, CSharpField] { def findGlobalTypes: Set[CSharpType] = namespaceToType.getOrElse(Constants.Global, Set.empty).toSet @targetName("appendAll") def ++=(other: CSharpProgramSummary): CSharpProgramSummary = { - new CSharpProgramSummary(ProgramSummary.merge(namespaceToType, other.namespaceToType), imports ++ other.imports) + new CSharpProgramSummary( + ProgramSummary.merge(namespaceToType, other.namespaceToType), + imports ++ other.imports, + globalImports ++ other.globalImports + ) } } @@ -47,9 +50,10 @@ object CSharpProgramSummary { def apply( namespaceToType: NamespaceToTypeMap = mutable.Map.empty, - imports: Set[String] = Set.empty + imports: Set[String] = Set.empty, + globalImports: Set[String] = Set.empty ): CSharpProgramSummary = - new CSharpProgramSummary(namespaceToType, imports) + new CSharpProgramSummary(namespaceToType, imports, globalImports) def apply(summaries: Iterable[CSharpProgramSummary]): CSharpProgramSummary = summaries.foldLeft(CSharpProgramSummary())(_ ++= _) diff --git a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/datastructures/CSharpScope.scala b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/datastructures/CSharpScope.scala index 7ebe276c53c6..a0840dfbdcbf 100644 --- a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/datastructures/CSharpScope.scala +++ b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/datastructures/CSharpScope.scala @@ -11,7 +11,10 @@ class CSharpScope(summary: CSharpProgramSummary) with TypedScope[CSharpMethod, CSharpField, CSharpType](summary) with OverloadableScope[CSharpMethod] { - override val typesInScope: mutable.Set[CSharpType] = mutable.Set.empty[CSharpType].addAll(summary.findGlobalTypes) + override val typesInScope: mutable.Set[CSharpType] = mutable.Set + .empty[CSharpType] + .addAll(summary.findGlobalTypes) + .addAll(summary.globalImports.flatMap(summary.namespaceToType.getOrElse(_, Set.empty))) /** @return * the surrounding type declaration if one exists. diff --git a/joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/UsingDirectiveTests.scala b/joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/UsingDirectiveTests.scala new file mode 100644 index 000000000000..73a55bec1a98 --- /dev/null +++ b/joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/UsingDirectiveTests.scala @@ -0,0 +1,59 @@ +package io.joern.csharpsrc2cpg.querying.ast + +import io.joern.csharpsrc2cpg.testfixtures.CSharpCode2CpgFixture +import io.shiftleft.semanticcpg.language.* + +class UsingDirectiveTests extends CSharpCode2CpgFixture { + + "`global using` directive in another file" should { + val cpg = code(""" + |class Foo + |{ + | static void Run() + | { + | Console.WriteLine("Hello"); + | } + |}""".stripMargin) + .moreCode( + """ + |global using System; + |""".stripMargin, + "globals.cs" + ) + + "make the imported namespace available in the current file" in { + inside(cpg.call("WriteLine").l) { + case writeLine :: Nil => + writeLine.methodFullName shouldBe "System.Console.WriteLine:System.Void(System.String)" + case xs => + fail(s"Expected single WriteLine call, but found $xs") + } + } + } + + "`using` directive in another file" should { + val cpg = code(""" + |class Foo + |{ + | static void Run() + | { + | Console.WriteLine("Hello"); + | } + |}""".stripMargin) + .moreCode( + """ + |using System; + |""".stripMargin, + "dummy.cs" + ) + + "not affect the imported namespaces in the current file" in { + inside(cpg.call("WriteLine").l) { + case writeLine :: Nil => + writeLine.methodFullName shouldBe ".WriteLine:" + case xs => + fail(s"Expected single WriteLine call, but found $xs") + } + } + } +}