forked from joernio/joern
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[c2cpg] Handle preprocessed files (joernio#4466)
cpp/gcc/g++ offer the -E switch. The -E switch forces the compiler to stop after the preprocessing phase and spit out the result where all preprocessor directives are expanded or processed, all macros expanded, and all header files included. Typically, this result is stored as *.i file. With this PR we process such files and exclude all the others with the exact same name if `--with-preprocessed-files` is used. Fixes: joernio#4462
- Loading branch information
1 parent
abf5a8d
commit 1b252c9
Showing
5 changed files
with
144 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 91 additions & 46 deletions
137
joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/passes/ast/FileTests.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,109 @@ | ||
package io.joern.c2cpg.passes.ast | ||
|
||
import io.joern.c2cpg.testfixtures.C2CpgSuite | ||
import io.shiftleft.semanticcpg.language._ | ||
import io.joern.c2cpg.Config | ||
import io.shiftleft.semanticcpg.language.* | ||
import io.shiftleft.semanticcpg.language.types.structure.FileTraversal | ||
import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal | ||
|
||
class FileTests extends C2CpgSuite { | ||
|
||
private val cpg = code(""" | ||
| int foo() {} | ||
| int bar() {} | ||
| struct my_struct { int x; }; | ||
|""".stripMargin) | ||
|
||
"should contain the correct file nodes" in { | ||
val List(fileTest, fileUnknown) = cpg.file.nameNot("<includes>").l | ||
fileTest.name shouldBe "Test0.c" | ||
fileTest.order shouldBe 0 | ||
fileUnknown.name shouldBe FileTraversal.UNKNOWN | ||
fileUnknown.order shouldBe 0 | ||
} | ||
"File test for single file" should { | ||
|
||
"should contain exactly one placeholder file node with `name=\"<unknown>\"/order=0`" in { | ||
cpg.file(FileTraversal.UNKNOWN).order.l shouldBe List(0) | ||
cpg.file(FileTraversal.UNKNOWN).hash.l shouldBe List() | ||
} | ||
val cpg = code(""" | ||
| int foo() {} | ||
| int bar() {} | ||
| struct my_struct { int x; }; | ||
|""".stripMargin) | ||
|
||
"should allow traversing from file to its namespace blocks" in { | ||
cpg.file.nameNot(FileTraversal.UNKNOWN).namespaceBlock.name.toSetMutable shouldBe Set( | ||
NamespaceTraversal.globalNamespaceName | ||
) | ||
} | ||
"contain the correct file nodes" in { | ||
val List(fileTest, fileUnknown) = cpg.file.nameNot("<includes>").l | ||
fileTest.name shouldBe "Test0.c" | ||
fileTest.order shouldBe 0 | ||
fileUnknown.name shouldBe FileTraversal.UNKNOWN | ||
fileUnknown.order shouldBe 0 | ||
} | ||
|
||
"contain exactly one placeholder file node with `name=\"<unknown>\"/order=0`" in { | ||
cpg.file(FileTraversal.UNKNOWN).order.l shouldBe List(0) | ||
cpg.file(FileTraversal.UNKNOWN).hash.l shouldBe List() | ||
} | ||
|
||
"allow traversing from file to its namespace blocks" in { | ||
cpg.file.nameNot(FileTraversal.UNKNOWN).namespaceBlock.name.toSetMutable shouldBe Set( | ||
NamespaceTraversal.globalNamespaceName | ||
) | ||
} | ||
|
||
"allow traversing from file to its methods via namespace block" in { | ||
cpg.file.nameNot(FileTraversal.UNKNOWN).method.name.toSetMutable shouldBe Set( | ||
NamespaceTraversal.globalNamespaceName, | ||
"foo", | ||
"bar" | ||
) | ||
} | ||
|
||
"allow traversing from file to its type declarations via namespace block" in { | ||
cpg.file | ||
.nameNot(FileTraversal.UNKNOWN) | ||
.typeDecl | ||
.nameNot(NamespaceTraversal.globalNamespaceName) | ||
.name | ||
.l | ||
.sorted shouldBe List("ANY", "int", "my_struct", "void") | ||
} | ||
|
||
"should allow traversing from file to its methods via namespace block" in { | ||
cpg.file.nameNot(FileTraversal.UNKNOWN).method.name.toSetMutable shouldBe Set( | ||
NamespaceTraversal.globalNamespaceName, | ||
"foo", | ||
"bar" | ||
) | ||
"allow traversing to namespaces" in { | ||
val List(ns1, ns2, ns3) = cpg.file.namespaceBlock.l | ||
ns1.filename shouldBe "Test0.c" | ||
ns1.fullName shouldBe "Test0.c:<global>" | ||
ns2.filename shouldBe "<includes>" | ||
ns2.fullName shouldBe "<includes>:<global>" | ||
ns3.filename shouldBe "<unknown>" | ||
ns3.fullName shouldBe "<global>" | ||
cpg.file.namespace.name(NamespaceTraversal.globalNamespaceName).l.size shouldBe 3 | ||
} | ||
} | ||
|
||
"should allow traversing from file to its type declarations via namespace block" in { | ||
cpg.file | ||
.nameNot(FileTraversal.UNKNOWN) | ||
.typeDecl | ||
.nameNot(NamespaceTraversal.globalNamespaceName) | ||
.name | ||
.l | ||
.sorted shouldBe List("ANY", "int", "my_struct", "void") | ||
"File test for multiple source files and preprocessed files" should { | ||
|
||
val cpg = code("int foo() {}", "main.c") | ||
.moreCode("int bar() {}", "main.cpp") | ||
.moreCode("int foo() {}", "main.i") | ||
.moreCode("int baz() {}", "main.h") | ||
.moreCode("int other() {}", "other.h") | ||
.moreCode("int other() {}", "other.i") | ||
.withConfig(Config(withPreprocessedFiles = true)) | ||
|
||
"contain the correct file nodes" in { | ||
cpg.method.nameNot("<global>").internal.name.sorted.l shouldBe List("foo", "other") | ||
cpg.file.nameNot("<includes>", "<unknown>").name.sorted.l shouldBe List("main.i", "other.i") | ||
} | ||
|
||
} | ||
|
||
"should allow traversing to namespaces" in { | ||
val List(ns1, ns2, ns3) = cpg.file.namespaceBlock.l | ||
ns1.filename shouldBe "Test0.c" | ||
ns1.fullName shouldBe "Test0.c:<global>" | ||
ns2.filename shouldBe "<includes>" | ||
ns2.fullName shouldBe "<includes>:<global>" | ||
ns3.filename shouldBe "<unknown>" | ||
ns3.fullName shouldBe "<global>" | ||
cpg.file.namespace.name(NamespaceTraversal.globalNamespaceName).l.size shouldBe 3 | ||
"File test for preprocessed files from C and CPP files" should { | ||
|
||
val cpg = code( | ||
""" | ||
|# 1 "a.c" 1 | ||
|int bar() {} | ||
|""".stripMargin, | ||
"a.i" | ||
).moreCode( | ||
""" | ||
|# 1 "b.cpp" 1 | ||
|class B {}; | ||
|""".stripMargin, | ||
"b.i" | ||
).withConfig(Config(withPreprocessedFiles = true)) | ||
|
||
"be parsed correctly" in { | ||
cpg.file.nameNot("<includes>", "<unknown>").name.sorted.l shouldBe List("a.i", "b.i") | ||
cpg.method.nameExact("bar").file.name.l shouldBe List("a.i") | ||
cpg.typeDecl.nameExact("B").file.name.l shouldBe List("b.i") | ||
} | ||
|
||
} | ||
|
||
} |