Skip to content

Commit 4352a20

Browse files
committed
C++: add support for C++17 fold expressions
1 parent 3905cf7 commit 4352a20

2 files changed

Lines changed: 66 additions & 0 deletions

File tree

cpp/ql/src/semmle/code/cpp/exprs/Expr.qll

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,65 @@ class NoExceptExpr extends Expr, @noexceptexpr {
964964
}
965965
}
966966

967+
/**
968+
* A C++17 fold expression.
969+
*/
970+
class FoldExpr extends Expr, @foldexpr {
971+
override string toString() {
972+
exists(string op |
973+
op = this.getOperatorString() and
974+
if this.isUnaryFold()
975+
then
976+
if this.isLeftFold()
977+
then result = "( ... " + op + " pack )"
978+
else result = "( pack " + op + " ... )"
979+
else
980+
if this.isLeftFold()
981+
then result = "( init " + op + " ... " + op + " pack )"
982+
else result = "( pack " + op + " ... " + op + " init )"
983+
)
984+
}
985+
986+
/** Gets the binary operator used in this fold expression, as a string. */
987+
string getOperatorString() { fold(underlyingElement(this), result, _) }
988+
989+
/** Holds if this is a left-fold expression. */
990+
predicate isLeftFold() { fold(underlyingElement(this), _, true) }
991+
992+
/** Holds if this is a right-fold expression. */
993+
predicate isRightFold() { fold(underlyingElement(this), _, false) }
994+
995+
/** Holds if this is a unary fold expression. */
996+
predicate isUnaryFold() { getNumChild() = 1 }
997+
998+
/** Holds if this is a binary fold expression. */
999+
predicate isBinaryFold() { getNumChild() = 2 }
1000+
1001+
/**
1002+
* Gets the child expression containing the unexpanded parameter pack.
1003+
*/
1004+
Expr getPackExpr() {
1005+
this.isUnaryFold() and
1006+
result = getChild(0)
1007+
or
1008+
this.isBinaryFold() and
1009+
if this.isRightFold() then result = getChild(0) else result = getChild(1)
1010+
}
1011+
1012+
/**
1013+
* If this is a binary fold, gets the expression representing the initial value.
1014+
*/
1015+
Expr getInitExpr() {
1016+
this.isBinaryFold() and
1017+
if this.isRightFold() then result = getChild(1) else result = getChild(0)
1018+
}
1019+
1020+
/**
1021+
* Holds if this is a binary fold with a child expression representing the initial value.
1022+
*/
1023+
predicate hasInitExpr() { exists(this.getInitExpr()) }
1024+
}
1025+
9671026
/**
9681027
* Holds if `child` is the `n`th child of `parent` in an alternative syntax
9691028
* tree that has `Conversion`s as part of the tree.

cpp/ql/src/semmlecode.cpp.dbscheme

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,7 @@ case @expr.kind of
14321432
| 129 = @new_array_expr
14331433
// ... 130 @objc_array_literal deprecated
14341434
// ... 131 @objc_dictionary_literal deprecated
1435+
| 132 = @foldexpr
14351436
// ...
14361437
| 200 = @ctordirectinit
14371438
| 201 = @ctorvirtualinit
@@ -1579,6 +1580,12 @@ lambda_capture(
15791580
@addressable = @function | @variable ;
15801581
@accessible = @addressable | @enumconstant ;
15811582

1583+
fold(
1584+
int expr: @foldexpr ref,
1585+
string operator: string ref,
1586+
boolean is_left_fold: boolean ref
1587+
);
1588+
15821589
stmts(
15831590
unique int id: @stmt,
15841591
int kind: int ref,

0 commit comments

Comments
 (0)