Skip to content

Commit

Permalink
[c2cpg] Remaining C++20 features (#5242)
Browse files Browse the repository at this point in the history
  • Loading branch information
max-leuthaeuser authored Jan 21, 2025
1 parent 6deaf3f commit 4339470
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 45 deletions.
8 changes: 4 additions & 4 deletions joern-cli/frontends/c2cpg/CPP_Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
| three-way comparison | [ ] |
| designated initializers | [~] |
| template syntax for lambdas | [ ] |
| range-based for loop with initializer | [?] |
| \[\[likely\]\] and \[\[unlikely\]\] attributes | [?] |
| deprecate implicit capture of this | [?] |
| class types in non-type template parameters | [?] |
| range-based for loop with initializer | [ ] |
| \[\[likely\]\] and \[\[unlikely\]\] attributes | [x] |
| deprecate implicit capture of this | [~] |
| class types in non-type template parameters | [~] |
| constexpr virtual functions | [~] |
| explicit(bool) | [ ] |
| immediate functions | [x] |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,16 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As

protected def cleanType(rawType: String): String = {
if (rawType == Defines.Any) return rawType
val tpe = ReservedKeywordsAtTypes.foldLeft(rawType) { (cur, repl) =>
val normalizedTpe = StringUtils.normalizeSpace(rawType.stripSuffix(" ()"))
val tpe = ReservedKeywordsAtTypes.foldLeft(normalizedTpe) { (cur, repl) =>
if (cur.startsWith(s"$repl ") || cur.contains(s" $repl ")) {
cur.replace(s" $repl ", " ").stripPrefix(s"$repl ")
} else cur
}
val normalizedTpe = StringUtils.normalizeSpace(tpe.stripSuffix(" ()"))
replaceWhitespaceAfterKeyword(normalizedTpe) match {
replaceWhitespaceAfterKeyword(tpe) match {
case "" => Defines.Any
case t if t.startsWith("[*this]") || t.startsWith("[this]") => normalizedTpe
case t if t.startsWith("[") && t.endsWith("]") => Defines.Array
case t if t.contains("->") => Defines.Function
case t if isThisLambdaCapture(t) || t.contains("->") => Defines.Function
case t if t.contains("?") => Defines.Any
case t if t.contains("#") => Defines.Any
case t if t.contains("::{") || t.contains("}::") => Defines.Any
Expand Down Expand Up @@ -187,6 +186,10 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
}
}

private def isThisLambdaCapture(tpe: String): Boolean = {
tpe.startsWith("[*this]") || tpe.startsWith("[this]") || (tpe.startsWith("[") && tpe.contains("this]"))
}

protected def safeGetEvaluation(expr: ICPPASTExpression): Option[ICPPEvaluation] = {
// In case of unresolved includes etc. this may fail throwing an unrecoverable exception
Try(expr.getEvaluation).toOption
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ class Cpp17FeaturesTests extends AstC2CpgSuite(fileSuffix = FileDefaults.CppExt)
|""".stripMargin)
// TODO: we can not express these lambda types in the current schema
// We would need to add a new type for lambdas that capture `this` by value copy/ref.
cpg.method.nameExact("getValueCopy").methodReturn.typeFullName.l shouldBe List("[*this] { return value; }")
cpg.method.nameExact("getValueRef").methodReturn.typeFullName.l shouldBe List("[this] { return value; }")
cpg.method.nameExact("getValueCopy").methodReturn.typeFullName.l shouldBe List(Defines.Function)
cpg.method.nameExact("getValueRef").methodReturn.typeFullName.l shouldBe List(Defines.Function)
}

"handle inline variables" in {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.joern.c2cpg.cpp.features20

import io.joern.c2cpg.astcreation.Defines
import io.joern.c2cpg.parser.FileDefaults
import io.joern.c2cpg.testfixtures.AstC2CpgSuite
import io.shiftleft.codepropertygraph.generated.ControlStructureTypes
Expand Down Expand Up @@ -210,59 +211,103 @@ class Cpp20FeaturesTests extends AstC2CpgSuite(fileSuffix = FileDefaults.CppExt)
}
}

"handle range-based for loop with initializer" ignore {
"handle range-based for loop with initializer" in {
val cpg = code("""
|for (auto v = std::vector{1, 2, 3}; auto& e : v) {
| std::cout << e;
|void foo() {
| for (auto v = list; auto& e : v) {
| std::cout << e;
| }
|}
|// prints "123"
|""".stripMargin)
???
pendingUntilFixed {
// range-based for loop with initializer can not be parsed at all by CDT at the moment
val List(v, e) = cpg.method.nameExact("foo").controlStructure.astChildren.isLocal.l
v.name shouldBe "v"
e.name shouldBe "e"
val List(vectorInitCall) =
cpg.method.nameExact("foo").controlStructure.astChildren.order(2).isCall.argument.isCall.l
vectorInitCall.argumentIndex shouldBe 2
vectorInitCall.name shouldBe Defines.OperatorConstructorInitializer
vectorInitCall.argument.code.l shouldBe List("1", "2", "3")
}
}

"handle likely and unlikely attributes" ignore {
"handle likely and unlikely attributes" in {
val cpg = code("""
|switch (n) {
|case 1:
| // ...
| break;
|
|[[likely]] case 2: // n == 2 is considered to be arbitrarily more
| // ... // likely than any other value of n
| break;
|}
|void foo() {
| switch (n) {
| case 1:
| case1();
| break;
| [[likely]] case 2: // n == 2 is considered to be arbitrarily more
| case2() // likely than any other value of n
| break;
| }
|
|int random = get_random_number_between_x_and_y(0, 3);
|if (random > 0) [[likely]] {
| // body of if statement
| // ...
|}
| int random = get_random_number_between_x_and_y(0, 3);
| if (random > 0) [[likely]] {
| // body of if statement
| likelyIf();
| }
|
|while (unlikely_truthy_condition) [[unlikely]] {
| // body of while statement
| // ...
| while (unlikely_truthy_condition) [[unlikely]] {
| // body of while statement
| unlikelyWhile()
| }
|}
|""".stripMargin)
???
val cases =
cpg.method
.nameExact("foo")
.controlStructure
.controlStructureTypeExact(ControlStructureTypes.SWITCH)
.astChildren
.isBlock
._jumpTargetViaAstOut
cases.code.l shouldBe List("case 1:", "[[likely]] case 2:")
cpg.method
.nameExact("foo")
.controlStructure
.controlStructureTypeExact(ControlStructureTypes.SWITCH)
.ast
.isCall
.code
.l shouldBe List("case1()", "case2()")

cpg.method
.nameExact("foo")
.controlStructure
.controlStructureTypeExact(ControlStructureTypes.IF)
.ast
.isCall
.code
.l shouldBe List("random > 0", "likelyIf()")

cpg.method
.nameExact("foo")
.controlStructure
.controlStructureTypeExact(ControlStructureTypes.WHILE)
.ast
.isCall
.code
.l shouldBe List("unlikelyWhile()")
}

"handle deprecate implicit capture of this" ignore {
"handle deprecate implicit capture of this" in {
val cpg = code("""
|struct int_value {
| int n = 0;
| auto getter_fn() {
| // BAD:
| // return [=]() { return n; };
|
| // GOOD:
| return [=, *this]() { return n; };
| }
|};
|""".stripMargin)
???
// TODO: we can not express these lambda types in the current schema
// We would need to add a new type for lambdas that capture `this`
cpg.method.nameExact("getter_fn").methodReturn.typeFullName.l shouldBe List(Defines.Function)
}

"handle class types in non-type template parameters" ignore {
"handle class types in non-type template parameters" in {
val cpg = code("""
|struct foo {
| foo() = default;
Expand All @@ -274,10 +319,23 @@ class Cpp20FeaturesTests extends AstC2CpgSuite(fileSuffix = FileDefaults.CppExt)
| return f;
|}
|
|get_foo(); // uses implicit constructor
|get_foo<foo{123}>();
|void main() {
| get_foo(); // uses implicit constructor
| get_foo<foo{123}>();
|}
|""".stripMargin)
???
cpg.typeDecl.nameExact("foo").size shouldBe 1
cpg.method.nameExact("get_foo").size shouldBe 1
cpg.method.nameExact("main").ast.isCall.typeFullName.l shouldBe List("ANY", "foo")
cpg.method.nameExact("main").ast.isCall.methodFullName.l shouldBe List(
// we can not resolve the implicit constructor call case:
"<unresolvedNamespace>.get_foo:<unresolvedSignature>(0)",
"get_foo:foo()"
)
pendingUntilFixed {
cpg.method.nameExact("main").ast.isCall.typeFullName.l shouldBe List("foo", "foo")
cpg.method.nameExact("main").ast.isCall.methodFullName.l shouldBe List("get_foo:foo()", "get_foo:foo()")
}
}

"handle constexpr virtual functions" in {
Expand Down

0 comments on commit 4339470

Please sign in to comment.