Skip to content

Commit

Permalink
[c#] support for <Using Include=XXX> in csproj files (#5240)
Browse files Browse the repository at this point in the history
  • Loading branch information
xavierpinho authored Jan 20, 2025
1 parent a498a15 commit 03c8d12
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
// <Using Include="System"/>
// <Using Remove="System"/>
// removes "System", whereas
// <Using Remove="System"/>
// <Using Include="System"/>
// 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
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
"""
|<Project Sdk="Microsoft.NET.Sdk">
| <PropertyGroup>
| <OutputType>Exe</OutputType>
| <ImplicitUsings>false</ImplicitUsings>
| </PropertyGroup>
| <ItemGroup>
| <Using Include="System" />
| </ItemGroup>
|</Project>
|""".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(
"""
|<Project Sdk="Microsoft.NET.Sdk">
| <PropertyGroup>
| <OutputType>Exe</OutputType>
| <ImplicitUsings>true</ImplicitUsings>
| </PropertyGroup>
| <ItemGroup>
| <Using Include="System" />
| <Using Remove="System" />
| </ItemGroup>
|</Project>
|""".stripMargin,
fileName = "App.csproj"
)

"not resolve WriteLine call" in {
cpg.call.nameExact("WriteLine").methodFullName.l shouldBe List(
"<unresolvedNamespace>.WriteLine:<unresolvedSignature>"
)
}
}

"accompanied by a NET.Sdk csproj with ImplicitUsings enabled but excluding and including `System`" should {
val cpg = code("""
|Console.WriteLine("Foo");
|""".stripMargin)
.moreCode(
"""
|<Project Sdk="Microsoft.NET.Sdk">
| <PropertyGroup>
| <OutputType>Exe</OutputType>
| <ImplicitUsings>true</ImplicitUsings>
| </PropertyGroup>
| <ItemGroup>
| <Using Remove="System" />
| <Using Include="System" />
| </ItemGroup>
|</Project>
|""".stripMargin,
fileName = "App.csproj"
)

"resolve WriteLine call" in {
cpg.call.nameExact("WriteLine").methodFullName.l shouldBe List(
"System.Console.WriteLine:System.Void(System.String)"
)
}
}
}

}

0 comments on commit 03c8d12

Please sign in to comment.