Skip to content

Commit 9e8e630

Browse files
authored
Merge pull request #1649 from ian-semmle/constexpr_if
C++: Add 'constexpr if' support
2 parents 6d10731 + 96a2123 commit 9e8e630

File tree

11 files changed

+4190
-101
lines changed

11 files changed

+4190
-101
lines changed

cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,84 @@ class IfStmt extends ConditionalStmt, @stmt_if {
294294
}
295295
}
296296

297+
/**
298+
* A C/C++ 'constexpr if' statement.
299+
*/
300+
class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
301+
302+
/**
303+
* Gets the condition expression of this 'constexpr if' statement.
304+
*
305+
* For example, for
306+
* ```
307+
* if constexpr (b) { x = 1; }
308+
* ```
309+
* the result is `b`.
310+
*/
311+
Expr getCondition() { result = this.getChild(0) }
312+
313+
override Expr getControllingExpr() { result = this.getCondition() }
314+
315+
/**
316+
* Gets the 'then' statement of this 'constexpr if' statement.
317+
*
318+
* For example, for
319+
* ```
320+
* if constexpr (b) { x = 1; }
321+
* ```
322+
* the result is the `Block` `{ x = 1; }`.
323+
*/
324+
Stmt getThen() { constexpr_if_then(underlyingElement(this), unresolveElement(result)) }
325+
326+
/**
327+
* Gets the 'else' statement of this 'constexpr if' statement, if any.
328+
*
329+
* For example, for
330+
* ```
331+
* if constexpr (b) { x = 1; } else { x = 2; }
332+
* ```
333+
* the result is the `Block` `{ x = 2; }`, and for
334+
* ```
335+
* if constexpr (b) { x = 1; }
336+
* ```
337+
* there is no result.
338+
*/
339+
Stmt getElse() { constexpr_if_else(underlyingElement(this), unresolveElement(result)) }
340+
341+
/**
342+
* Holds if this 'constexpr if' statement has an 'else' statement.
343+
*
344+
* For example, this holds for
345+
* ```
346+
* if constexpr (b) { x = 1; } else { x = 2; }
347+
* ```
348+
* but not for
349+
* ```
350+
* if constexpr (b) { x = 1; }
351+
* ```
352+
*/
353+
predicate hasElse() { exists(Stmt s | this.getElse() = s) }
354+
355+
override string toString() { result = "if constexpr (...) ... " }
356+
357+
override predicate mayBeImpure() {
358+
this.getCondition().mayBeImpure() or
359+
this.getThen().mayBeImpure() or
360+
this.getElse().mayBeImpure()
361+
}
362+
override predicate mayBeGloballyImpure() {
363+
this.getCondition().mayBeGloballyImpure() or
364+
this.getThen().mayBeGloballyImpure() or
365+
this.getElse().mayBeGloballyImpure()
366+
}
367+
368+
override MacroInvocation getGeneratingMacro() {
369+
result.getAnExpandedElement() = this.getCondition() and
370+
this.getThen().getGeneratingMacro() = result and
371+
(this.hasElse() implies this.getElse().getGeneratingMacro() = result)
372+
}
373+
}
374+
297375
/**
298376
* A C/C++ loop, that is, either a 'while' loop, a 'for' loop, or a
299377
* 'do' loop.

cpp/ql/src/semmlecode.cpp.dbscheme

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,7 @@ case @stmt.kind of
16391639
// ... 32 @stmt_at_synchronized deprecated
16401640
| 33 = @stmt_handler
16411641
// ... 34 @stmt_finally_end deprecated
1642+
| 35 = @stmt_constexpr_if
16421643
;
16431644

16441645
type_vla(
@@ -1661,6 +1662,16 @@ if_else(
16611662
int else_id: @stmt ref
16621663
);
16631664

1665+
constexpr_if_then(
1666+
unique int constexpr_if_stmt: @stmt_constexpr_if ref,
1667+
int then_id: @stmt ref
1668+
);
1669+
1670+
constexpr_if_else(
1671+
unique int constexpr_if_stmt: @stmt_constexpr_if ref,
1672+
int else_id: @stmt ref
1673+
);
1674+
16641675
while_body(
16651676
unique int while_stmt: @stmt_while ref,
16661677
int body_id: @stmt ref

0 commit comments

Comments
 (0)