mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
C++: IR support for range-based for loops
IR construction was missing support for C++ 11 range-based `for` loops. The extractor generates ASTs for the compiler-generated implementation already, so I had enough information to generate IR. I've expanded on some of the predicates in `RangeBasedForStmt` to access the desugared information. One complication was that the `DeclStmt`s for the compiler-generated variables seem to have results for `getDeclaration()` but not for `getDeclarationEntry()`. This required handling these slightly differently than we do for other `DeclStmt`s. The flow for range-based `for` is actually easier than for a regular `for`, because all three components (init, condition, and update) are always present.
This commit is contained in:
@@ -3,6 +3,7 @@ import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedStmt
|
||||
@@ -159,6 +160,23 @@ cached private module Cached {
|
||||
)
|
||||
)
|
||||
or
|
||||
// Range-based for loop:
|
||||
// Any edge from within the update of the loop to the condition of
|
||||
// the loop is a back edge.
|
||||
exists(TranslatedRangeBasedForStmt s, TranslatedCondition condition |
|
||||
s instanceof TranslatedRangeBasedForStmt and
|
||||
condition = s.getCondition() and
|
||||
result = condition.getFirstInstruction() and
|
||||
exists(TranslatedElement inUpdate, InstructionTag tag |
|
||||
result = inUpdate.getInstructionSuccessor(tag, kind) and
|
||||
exists(TranslatedElement update |
|
||||
update = s.getUpdate() |
|
||||
inUpdate = update.getAChild*()
|
||||
) and
|
||||
instruction = inUpdate.getInstruction(tag)
|
||||
)
|
||||
)
|
||||
or
|
||||
// Goto statement:
|
||||
// As a conservative approximation, any edge out of `goto` is a back edge
|
||||
// unless it goes strictly forward in the program text. A `goto` whose
|
||||
|
||||
@@ -191,6 +191,44 @@ class TranslatedVariableDeclarationEntry extends TranslatedVariableDeclaration,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedRangeBasedForVariableDeclaration` that represents the declaration of
|
||||
* `var`.
|
||||
*/
|
||||
TranslatedRangeBasedForVariableDeclaration getTranslatedRangeBasedForVariableDeclaration(
|
||||
LocalVariable var) {
|
||||
result.getVariable() = var
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a compiler-generated variable in a range-based `for` loop.
|
||||
*/
|
||||
class TranslatedRangeBasedForVariableDeclaration extends TranslatedVariableDeclaration,
|
||||
TTranslatedRangeBasedForVariableDeclaration {
|
||||
RangeBasedForStmt forStmt;
|
||||
LocalVariable var;
|
||||
|
||||
TranslatedRangeBasedForVariableDeclaration() {
|
||||
this = TTranslatedRangeBasedForVariableDeclaration(forStmt, var)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = var.toString()
|
||||
}
|
||||
|
||||
override Locatable getAST() {
|
||||
result = var
|
||||
}
|
||||
|
||||
override Function getFunction() {
|
||||
result = forStmt.getEnclosingFunction()
|
||||
}
|
||||
|
||||
override LocalVariable getVariable() {
|
||||
result = var
|
||||
}
|
||||
}
|
||||
|
||||
TranslatedConditionDecl getTranslatedConditionDecl(ConditionDeclExpr expr) {
|
||||
result.getAST() = expr
|
||||
}
|
||||
|
||||
@@ -359,6 +359,16 @@ newtype TTranslatedElement =
|
||||
declStmt.getADeclarationEntry() = entry
|
||||
)
|
||||
} or
|
||||
// A compiler-generated variable to implement a range-based for loop. These don't have a
|
||||
// `DeclarationEntry` in the database, so we have to go by the `Variable` itself.
|
||||
TTranslatedRangeBasedForVariableDeclaration(RangeBasedForStmt forStmt, LocalVariable var) {
|
||||
translateStmt(forStmt) and
|
||||
(
|
||||
var = forStmt.getRangeVariable() or
|
||||
var = forStmt.getBeginEndDeclaration().getADeclaration() or
|
||||
var = forStmt.getVariable()
|
||||
)
|
||||
} or
|
||||
// An allocator call in a `new` or `new[]` expression
|
||||
TTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
|
||||
not ignoreExpr(newExpr)
|
||||
|
||||
@@ -630,6 +630,105 @@ class TranslatedForStmt extends TranslatedLoop {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a range-based `for` loop.
|
||||
* Note that this class does not extend `TranslatedLoop`. This is because the "body" of the
|
||||
* range-based `for` loop consists of the per-iteration variable declaration followed by the
|
||||
* user-written body statement. It is easier to handle the control flow of the loop separately,
|
||||
* rather than synthesizing a single body or complicating the interface of `TranslatedLoop`.
|
||||
*/
|
||||
class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
||||
override RangeBasedForStmt stmt;
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getRangeVariableDeclaration() or
|
||||
id = 1 and result = getBeginVariableDeclaration() or
|
||||
id = 2 and result = getEndVariableDeclaration() or
|
||||
id = 3 and result = getCondition() or
|
||||
id = 4 and result = getUpdate() or
|
||||
id = 5 and result = getVariableDeclaration() or
|
||||
id = 6 and result = getBody()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
result = getRangeVariableDeclaration().getFirstInstruction()
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
(
|
||||
child = getRangeVariableDeclaration() and
|
||||
result = getBeginVariableDeclaration().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
child = getBeginVariableDeclaration() and
|
||||
result = getEndVariableDeclaration().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
child = getEndVariableDeclaration() and
|
||||
result = getCondition().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
child = getVariableDeclaration() and
|
||||
result = getBody().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
child = getBody() and
|
||||
result = getUpdate().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
child = getUpdate() and
|
||||
result = getCondition().getFirstInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType,
|
||||
boolean isGLValue) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getChildTrueSuccessor(TranslatedCondition child) {
|
||||
child = getCondition() and result = getVariableDeclaration().getFirstInstruction()
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(TranslatedCondition child) {
|
||||
child = getCondition() and result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
private TranslatedRangeBasedForVariableDeclaration getRangeVariableDeclaration() {
|
||||
result = getTranslatedRangeBasedForVariableDeclaration(stmt.getRangeVariable())
|
||||
}
|
||||
|
||||
private TranslatedRangeBasedForVariableDeclaration getBeginVariableDeclaration() {
|
||||
result = getTranslatedRangeBasedForVariableDeclaration(stmt.getBeginVariable())
|
||||
}
|
||||
|
||||
private TranslatedRangeBasedForVariableDeclaration getEndVariableDeclaration() {
|
||||
result = getTranslatedRangeBasedForVariableDeclaration(stmt.getEndVariable())
|
||||
}
|
||||
|
||||
// Public for getInstructionBackEdgeSuccessor
|
||||
final TranslatedCondition getCondition() {
|
||||
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
|
||||
}
|
||||
|
||||
// Public for getInstructionBackEdgeSuccessor
|
||||
final TranslatedExpr getUpdate() {
|
||||
result = getTranslatedExpr(stmt.getUpdate().getFullyConverted())
|
||||
}
|
||||
|
||||
private TranslatedRangeBasedForVariableDeclaration getVariableDeclaration() {
|
||||
result = getTranslatedRangeBasedForVariableDeclaration(stmt.getVariable())
|
||||
}
|
||||
|
||||
private TranslatedStmt getBody() {
|
||||
result = getTranslatedStmt(stmt.getStmt())
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedJumpStmt extends TranslatedStmt {
|
||||
override JumpStmt stmt;
|
||||
|
||||
|
||||
@@ -675,7 +675,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
* ```
|
||||
* the result is `int x`.
|
||||
*/
|
||||
Variable getVariable() { result = getChild(4).(DeclStmt).getADeclaration() }
|
||||
LocalVariable getVariable() { result = getChild(4).(DeclStmt).getADeclaration() }
|
||||
|
||||
/**
|
||||
* Gets the expression giving the range to iterate over.
|
||||
@@ -689,7 +689,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
Expr getRange() { result = getRangeVariable().getInitializer().getExpr() }
|
||||
|
||||
/** Gets the compiler-generated `__range` variable after desugaring. */
|
||||
Variable getRangeVariable() {
|
||||
LocalVariable getRangeVariable() {
|
||||
result = getChild(0).(DeclStmt).getADeclaration()
|
||||
}
|
||||
|
||||
@@ -709,6 +709,16 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
*/
|
||||
DeclStmt getBeginEndDeclaration() { result = this.getChild(1) }
|
||||
|
||||
/** Gets the compiler-generated `__begin` variable after desugaring. */
|
||||
LocalVariable getBeginVariable() {
|
||||
result = getBeginEndDeclaration().getDeclaration(0)
|
||||
}
|
||||
|
||||
/** Gets the compiler-generated `__end` variable after desugaring. */
|
||||
LocalVariable getEndVariable() {
|
||||
result = getBeginEndDeclaration().getDeclaration(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the compiler-generated `++__begin` which is the update
|
||||
* expression of this for statement after desugaring. It will
|
||||
@@ -718,8 +728,8 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
Expr getUpdate() { result = this.getChild(3) }
|
||||
|
||||
/** Gets the compiler-generated `__begin` variable after desugaring. */
|
||||
Variable getAnIterationVariable() {
|
||||
result = getUpdate().getAChild().(VariableAccess).getTarget()
|
||||
LocalVariable getAnIterationVariable() {
|
||||
result = getBeginVariable()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user