mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Merge pull request #1287 from nickrolfe/fold
C++: support for fold expressions
This commit is contained in:
@@ -33,3 +33,4 @@
|
||||
- Additional support for definition by reference has been added to the `semmle.code.cpp.dataflow.TaintTracking` library.
|
||||
- The taint tracking library now includes taint-specific edges for functions modeled in `semmle.code.cpp.models.interfaces.DataFlow`.
|
||||
- The taint tracking library adds flow through library functions that are modeled in `semmle.code.cpp.models.interfaces.Taint`. Queries can add subclasses of `TaintFunction` to specify additional flow.
|
||||
- There is a new `FoldExpr` class, representing C++17 fold expressions.
|
||||
|
||||
@@ -964,6 +964,61 @@ class NoExceptExpr extends Expr, @noexceptexpr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++17 fold expression. This will only appear in an uninstantiated template; any instantiations
|
||||
* of the template will instead contain the sequence of expressions given by expanding the fold.
|
||||
*/
|
||||
class FoldExpr extends Expr, @foldexpr {
|
||||
override string toString() {
|
||||
exists(string op |
|
||||
op = this.getOperatorString() and
|
||||
if this.isUnaryFold()
|
||||
then
|
||||
if this.isLeftFold()
|
||||
then result = "( ... " + op + " pack )"
|
||||
else result = "( pack " + op + " ... )"
|
||||
else
|
||||
if this.isLeftFold()
|
||||
then result = "( init " + op + " ... " + op + " pack )"
|
||||
else result = "( pack " + op + " ... " + op + " init )"
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the binary operator used in this fold expression, as a string. */
|
||||
string getOperatorString() { fold(underlyingElement(this), result, _) }
|
||||
|
||||
/** Holds if this is a left-fold expression. */
|
||||
predicate isLeftFold() { fold(underlyingElement(this), _, true) }
|
||||
|
||||
/** Holds if this is a right-fold expression. */
|
||||
predicate isRightFold() { fold(underlyingElement(this), _, false) }
|
||||
|
||||
/** Holds if this is a unary fold expression. */
|
||||
predicate isUnaryFold() { getNumChild() = 1 }
|
||||
|
||||
/** Holds if this is a binary fold expression. */
|
||||
predicate isBinaryFold() { getNumChild() = 2 }
|
||||
|
||||
/**
|
||||
* Gets the child expression containing the unexpanded parameter pack.
|
||||
*/
|
||||
Expr getPackExpr() {
|
||||
this.isUnaryFold() and
|
||||
result = getChild(0)
|
||||
or
|
||||
this.isBinaryFold() and
|
||||
if this.isRightFold() then result = getChild(0) else result = getChild(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a binary fold, gets the expression representing the initial value.
|
||||
*/
|
||||
Expr getInitExpr() {
|
||||
this.isBinaryFold() and
|
||||
if this.isRightFold() then result = getChild(1) else result = getChild(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `child` is the `n`th child of `parent` in an alternative syntax
|
||||
* tree that has `Conversion`s as part of the tree.
|
||||
|
||||
@@ -1432,6 +1432,7 @@ case @expr.kind of
|
||||
| 129 = @new_array_expr
|
||||
// ... 130 @objc_array_literal deprecated
|
||||
// ... 131 @objc_dictionary_literal deprecated
|
||||
| 132 = @foldexpr
|
||||
// ...
|
||||
| 200 = @ctordirectinit
|
||||
| 201 = @ctorvirtualinit
|
||||
@@ -1579,6 +1580,12 @@ lambda_capture(
|
||||
@addressable = @function | @variable ;
|
||||
@accessible = @addressable | @enumconstant ;
|
||||
|
||||
fold(
|
||||
int expr: @foldexpr ref,
|
||||
string operator: string ref,
|
||||
boolean is_left_fold: boolean ref
|
||||
);
|
||||
|
||||
stmts(
|
||||
unique int id: @stmt,
|
||||
int kind: int ref,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
28
cpp/ql/test/library-tests/fold/fold.cpp
Normal file
28
cpp/ql/test/library-tests/fold/fold.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// semmle-extractor-options: --edg --c++17
|
||||
|
||||
template<typename ...Args>
|
||||
int sum(Args&&... args) {
|
||||
return (args + ...);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
int product(Args&&... args) {
|
||||
return (... * args);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
bool all(Args&&... args) {
|
||||
return (args && ... && true);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
bool any(Args&&... args) {
|
||||
return (false || ... || args);
|
||||
}
|
||||
|
||||
void f() {
|
||||
int x = sum(1, 2, 3, 4, 5);
|
||||
int y = product(2, 4, 6, 8);
|
||||
bool a = all(true, true, false, true);
|
||||
bool b = any(false, true, false, false);
|
||||
}
|
||||
4
cpp/ql/test/library-tests/fold/fold.expected
Normal file
4
cpp/ql/test/library-tests/fold/fold.expected
Normal file
@@ -0,0 +1,4 @@
|
||||
| fold.cpp:5:10:5:21 | ( pack + ... ) | + | fold.cpp:5:11:5:14 | args | <no init> |
|
||||
| fold.cpp:10:10:10:21 | ( ... * pack ) | * | fold.cpp:10:17:10:20 | args | <no init> |
|
||||
| fold.cpp:15:10:15:30 | ( pack && ... && init ) | && | fold.cpp:15:11:15:14 | args | 1 |
|
||||
| fold.cpp:20:10:20:31 | ( init \|\| ... \|\| pack ) | \|\| | fold.cpp:20:27:20:30 | args | 0 |
|
||||
7
cpp/ql/test/library-tests/fold/fold.ql
Normal file
7
cpp/ql/test/library-tests/fold/fold.ql
Normal file
@@ -0,0 +1,7 @@
|
||||
import cpp
|
||||
|
||||
from FoldExpr fe, Expr pack, string init
|
||||
where
|
||||
pack = fe.getPackExpr() and
|
||||
if exists(fe.getInitExpr()) then init = fe.getInitExpr().toString() else init = "<no init>"
|
||||
select fe, fe.getOperatorString(), pack, init
|
||||
Reference in New Issue
Block a user