diff --git a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/utils/ImplicitUsingsCollector.scala b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/utils/ImplicitUsingsCollector.scala
index 8d921cbc48d8..14c4c26ce2b2 100644
--- a/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/utils/ImplicitUsingsCollector.scala
+++ b/joern-cli/frontends/csharpsrc2cpg/src/main/scala/io/joern/csharpsrc2cpg/utils/ImplicitUsingsCollector.scala
@@ -76,21 +76,40 @@ object ImplicitUsingsCollector {
.collect { case x if x.label == "ImplicitUsings" => x.text }
.exists(x => x == "true" || x == "enable")
- val exclusions = rootElem.child
+ val usingsFromProjectType = if (projectType.isDefined && implicitUsingsEnabled) {
+ projectTypeMapping.getOrElse(projectType.get, Nil)
+ } else {
+ Nil
+ }
+
+ // Once we gather the initial set of implicit usings (if any) based on the project type, we
+ // process ItemGroup.Using tags. The order in which we process these matters, e.g.
+ //
+ //
+ // removes "System", whereas
+ //
+ //
+ // adds "System".
+
+ rootElem.child
.collect { case x if x.label == "ItemGroup" => x.child }
.flatten
- .collect {
- case x if x.label == "Using" && x.attribute("Remove").isDefined =>
- x.attribute("Remove").flatMap(_.headOption.map(_.text))
- }
+ .collect { case x if x.label == "Using" => x }
.flatten
+ .foldLeft(usingsFromProjectType.toSet) { case (acc, node) =>
+ if (node.attribute("Remove").isDefined) {
+ node.attribute("Remove").flatMap(_.headOption.map(_.text)) match
+ case None => acc
+ case Some(toRemove) => acc.excl(toRemove)
+ } else if (node.attribute("Include").isDefined) {
+ node.attribute("Include").flatMap(_.headOption.map(_.text)) match
+ case None => acc
+ case Some(toInclude) => acc + toInclude
+ } else {
+ acc
+ }
+ }
.toList
-
- if (projectType.isDefined && implicitUsingsEnabled) {
- projectTypeMapping.getOrElse(projectType.get, Nil).diff(exclusions)
- } else {
- Nil
- }
}
}
diff --git a/joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/ImplicitUsingsTests.scala b/joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/ImplicitUsingsTests.scala
index 8a0637c001f8..ac5ce4015941 100644
--- a/joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/ImplicitUsingsTests.scala
+++ b/joern-cli/frontends/csharpsrc2cpg/src/test/scala/io/joern/csharpsrc2cpg/querying/ast/ImplicitUsingsTests.scala
@@ -147,6 +147,86 @@ class ImplicitUsingsTests extends CSharpCode2CpgFixture {
)
}
}
+
+ "accompanied by a NET.Sdk csproj with ImplicitUsings disabled but including `System`" should {
+ val cpg = code("""
+ |Console.WriteLine("Foo");
+ |""".stripMargin)
+ .moreCode(
+ """
+ |
+ |
+ | Exe
+ | false
+ |
+ |
+ |
+ |
+ |
+ |""".stripMargin,
+ fileName = "App.csproj"
+ )
+
+ "resolve WriteLine call" in {
+ cpg.call.nameExact("WriteLine").methodFullName.l shouldBe List(
+ "System.Console.WriteLine:System.Void(System.String)"
+ )
+ }
+ }
+
+ "accompanied by a NET.Sdk csproj with ImplicitUsings enabled but including and excluding `System`" should {
+ val cpg = code("""
+ |Console.WriteLine("Foo");
+ |""".stripMargin)
+ .moreCode(
+ """
+ |
+ |
+ | Exe
+ | true
+ |
+ |
+ |
+ |
+ |
+ |
+ |""".stripMargin,
+ fileName = "App.csproj"
+ )
+
+ "not resolve WriteLine call" in {
+ cpg.call.nameExact("WriteLine").methodFullName.l shouldBe List(
+ ".WriteLine:"
+ )
+ }
+ }
+
+ "accompanied by a NET.Sdk csproj with ImplicitUsings enabled but excluding and including `System`" should {
+ val cpg = code("""
+ |Console.WriteLine("Foo");
+ |""".stripMargin)
+ .moreCode(
+ """
+ |
+ |
+ | Exe
+ | true
+ |
+ |
+ |
+ |
+ |
+ |
+ |""".stripMargin,
+ fileName = "App.csproj"
+ )
+
+ "resolve WriteLine call" in {
+ cpg.call.nameExact("WriteLine").methodFullName.l shouldBe List(
+ "System.Console.WriteLine:System.Void(System.String)"
+ )
+ }
+ }
}
}