Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[c#] support for global using directives in CSharpProgramSummary #5187

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -78,7 +80,7 @@ trait AstSummaryVisitor(implicit withSchemaValidation: ValidationMode) { this: A
})
})
.asInstanceOf[NamespaceToTypeMap]
CSharpProgramSummary(mapping, imports)
CSharpProgramSummary(mapping, imports, globalImports)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I believe the original design choice was to assign globals to the global namespace. Does this not work for your use-case?

Copy link
Contributor Author

@xavierpinho xavierpinho Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. From what I've seen, this Global namespace is being used to store top-level user-defined types found during pre-analysis. For using global XXX, we want to have XXX available in typesInScope but XXX is to be found in the program summary's NamespaceToTypeMap. Perhaps the best patch would be to add globalImports directly to typesInScope -- currently it's set to this findGlobalTypes. What do you reckon?

To illustrate, the newer patch I'm proposing would look something like (in CSharpScope.scala):

  override val typesInScope: mutable.Set[CSharpType] = {
    val globalTypes = summary.findGlobalTypes
    val globallyImportedTypes = summary.globalImports.flatMap(summary.namespaceToType.getOrElse(_, Set.empty))
    val result = mutable.Set.empty[CSharpType]
    result.addAll(globalTypes)
    result.addAll(globallyImportedTypes)
    result
  }

EDIT: See 3e016b4


@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
)
}

}
Expand All @@ -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())(_ ++= _)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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 "<unresolvedNamespace>.WriteLine:<unresolvedSignature>"
case xs =>
fail(s"Expected single WriteLine call, but found $xs")
}
}
}
}
Loading