Skip to content

Commit 71c019e

Browse files
committed
C++: Handle C++17 switch initializers
1 parent ebbd9c5 commit 71c019e

File tree

11 files changed

+568
-12
lines changed

11 files changed

+568
-12
lines changed

cpp/ql/lib/semmle/code/cpp/PrintAST.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,8 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred)
679679
or
680680
s.(IfStmt).getElse() = e and pred = "getElse()"
681681
or
682+
s.(SwitchStmt).getInitialization() = e and pred = "getInitialization()"
683+
or
682684
s.(SwitchStmt).getExpr() = e and pred = "getExpr()"
683685
or
684686
s.(SwitchStmt).getStmt() = e and pred = "getStmt()"

cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -708,30 +708,33 @@ private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) {
708708
or
709709
scope =
710710
any(SwitchStmt s |
711+
// SwitchStmt [-> init] -> expr
711712
i = -1 and ni = s and spec.isAt()
712713
or
713-
i = 0 and ni = s.getExpr() and spec.isAround()
714+
i = 0 and ni = s.getInitialization() and spec.isAround()
715+
or
716+
i = 1 and ni = s.getExpr() and spec.isAround()
714717
or
715718
// If the switch body is not a block then this step is skipped, and the
716719
// expression jumps directly to the cases.
717-
i = 1 and ni = s.getStmt().(BlockStmt) and spec.isAt()
720+
i = 2 and ni = s.getStmt().(BlockStmt) and spec.isAt()
718721
or
719-
i = 2 and ni = s.getASwitchCase() and spec.isBefore()
722+
i = 3 and ni = s.getASwitchCase() and spec.isBefore()
720723
or
721724
// If there is no default case, we can jump to after the block. Note: `i`
722725
// is same value as above.
723726
not s.getASwitchCase() instanceof DefaultCase and
724-
i = 2 and
727+
i = 3 and
725728
ni = s.getStmt() and
726729
spec.isAfter()
727730
or
728-
i = 3 and /* BARRIER */ ni = s and spec.isBarrier()
731+
i = 4 and /* BARRIER */ ni = s and spec.isBarrier()
729732
or
730-
i = 4 and ni = s.getStmt() and spec.isAfter()
733+
i = 5 and ni = s.getStmt() and spec.isAfter()
731734
or
732-
i = 5 and ni = s and spec.isAroundDestructors()
735+
i = 6 and ni = s and spec.isAroundDestructors()
733736
or
734-
i = 6 and ni = s and spec.isAfter()
737+
i = 7 and ni = s and spec.isAfter()
735738
)
736739
or
737740
scope =

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -717,14 +717,28 @@ class TranslatedSwitchStmt extends TranslatedStmt {
717717
result = getTranslatedExpr(stmt.getExpr().getFullyConverted())
718718
}
719719

720+
private Instruction getFirstExprInstruction() { result = getExpr().getFirstInstruction() }
721+
720722
private TranslatedStmt getBody() { result = getTranslatedStmt(stmt.getStmt()) }
721723

722-
override Instruction getFirstInstruction() { result = getExpr().getFirstInstruction() }
724+
override Instruction getFirstInstruction() {
725+
if hasInitialization()
726+
then result = getInitialization().getFirstInstruction()
727+
else result = getFirstExprInstruction()
728+
}
723729

724730
override TranslatedElement getChild(int id) {
725-
id = 0 and result = getExpr()
731+
id = 0 and result = getInitialization()
726732
or
727-
id = 1 and result = getBody()
733+
id = 1 and result = getExpr()
734+
or
735+
id = 2 and result = getBody()
736+
}
737+
738+
private predicate hasInitialization() { exists(stmt.getInitialization()) }
739+
740+
private TranslatedStmt getInitialization() {
741+
result = getTranslatedStmt(stmt.getInitialization())
728742
}
729743

730744
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -754,6 +768,8 @@ class TranslatedSwitchStmt extends TranslatedStmt {
754768
}
755769

756770
override Instruction getChildSuccessor(TranslatedElement child) {
771+
child = getInitialization() and result = getFirstExprInstruction()
772+
or
757773
child = getExpr() and result = getInstruction(SwitchBranchTag())
758774
or
759775
child = getBody() and result = getParent().getChildSuccessor(this)

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,28 @@ class DefaultCase extends SwitchCase {
15121512
class SwitchStmt extends ConditionalStmt, @stmt_switch {
15131513
override string getAPrimaryQlClass() { result = "SwitchStmt" }
15141514

1515+
/**
1516+
* Gets the initialization statement of this 'switch' statement.
1517+
*
1518+
* For example, for
1519+
* ```
1520+
* switch (x = y; b) { }
1521+
* ```
1522+
* the result is `x = y;`.
1523+
*
1524+
* Does not hold if the initialization statement is missing or an empty statement, as in
1525+
* ```
1526+
* switch (b) { }
1527+
* ```
1528+
* or
1529+
* ```
1530+
* switch (; b) { }
1531+
* ```
1532+
*/
1533+
Stmt getInitialization() {
1534+
switch_initialization(underlyingElement(this), unresolveElement(result))
1535+
}
1536+
15151537
/**
15161538
* Gets the expression that this 'switch' statement switches on.
15171539
*
@@ -1527,7 +1549,7 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch {
15271549
* ```
15281550
* the result is `i`.
15291551
*/
1530-
Expr getExpr() { result = this.getChild(0) }
1552+
Expr getExpr() { result = this.getChild(1) }
15311553

15321554
override Expr getControllingExpr() { result = this.getExpr() }
15331555

cpp/ql/lib/semmlecode.cpp.dbscheme

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1903,6 +1903,11 @@ do_body(
19031903
int body_id: @stmt ref
19041904
);
19051905

1906+
switch_initialization(
1907+
unique int switch_stmt: @stmt_switch ref,
1908+
int init_id: @stmt ref
1909+
);
1910+
19061911
#keyset[switch_stmt, index]
19071912
switch_case(
19081913
int switch_stmt: @stmt_switch ref,

cpp/ql/test/library-tests/ir/ir/PrintAST.expected

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13776,6 +13776,205 @@ ir.cpp:
1377613776
# 1781| Type = [IntType] int
1377713777
# 1781| ValueCategory = prvalue(load)
1377813778
# 1783| getStmt(8): [ReturnStmt] return ...
13779+
# 1785| [TopLevelFunction] void switch_initialization(int)
13780+
# 1785| <params>:
13781+
# 1785| getParameter(0): [Parameter] x
13782+
# 1785| Type = [IntType] int
13783+
# 1785| getEntryPoint(): [BlockStmt] { ... }
13784+
# 1786| getStmt(0): [SwitchStmt] switch (...) ...
13785+
# 1786| getInitialization(): [DeclStmt] declaration
13786+
# 1786| getDeclarationEntry(0): [VariableDeclarationEntry] definition of y
13787+
# 1786| Type = [IntType] int
13788+
# 1786| getVariable().getInitializer(): [Initializer] initializer for y
13789+
# 1786| getExpr(): [VariableAccess] x
13790+
# 1786| Type = [IntType] int
13791+
# 1786| ValueCategory = prvalue(load)
13792+
# 1786| getExpr(): [AddExpr] ... + ...
13793+
# 1786| Type = [IntType] int
13794+
# 1786| ValueCategory = prvalue
13795+
# 1786| getLeftOperand(): [VariableAccess] x
13796+
# 1786| Type = [IntType] int
13797+
# 1786| ValueCategory = prvalue(load)
13798+
# 1786| getRightOperand(): [Literal] 1
13799+
# 1786| Type = [IntType] int
13800+
# 1786| Value = [Literal] 1
13801+
# 1786| ValueCategory = prvalue
13802+
# 1786| getStmt(): [BlockStmt] { ... }
13803+
# 1787| getStmt(0): [SwitchCase] default:
13804+
# 1788| getStmt(1): [ExprStmt] ExprStmt
13805+
# 1788| getExpr(): [AssignExpr] ... = ...
13806+
# 1788| Type = [IntType] int
13807+
# 1788| ValueCategory = lvalue
13808+
# 1788| getLValue(): [VariableAccess] x
13809+
# 1788| Type = [IntType] int
13810+
# 1788| ValueCategory = lvalue
13811+
# 1788| getRValue(): [AddExpr] ... + ...
13812+
# 1788| Type = [IntType] int
13813+
# 1788| ValueCategory = prvalue
13814+
# 1788| getLeftOperand(): [VariableAccess] x
13815+
# 1788| Type = [IntType] int
13816+
# 1788| ValueCategory = prvalue(load)
13817+
# 1788| getRightOperand(): [VariableAccess] y
13818+
# 1788| Type = [IntType] int
13819+
# 1788| ValueCategory = prvalue(load)
13820+
# 1791| getStmt(1): [DeclStmt] declaration
13821+
# 1791| getDeclarationEntry(0): [VariableDeclarationEntry] definition of w
13822+
# 1791| Type = [IntType] int
13823+
# 1792| getStmt(2): [SwitchStmt] switch (...) ...
13824+
# 1792| getInitialization(): [ExprStmt] ExprStmt
13825+
# 1792| getExpr(): [AssignExpr] ... = ...
13826+
# 1792| Type = [IntType] int
13827+
# 1792| ValueCategory = lvalue
13828+
# 1792| getLValue(): [VariableAccess] w
13829+
# 1792| Type = [IntType] int
13830+
# 1792| ValueCategory = lvalue
13831+
# 1792| getRValue(): [VariableAccess] x
13832+
# 1792| Type = [IntType] int
13833+
# 1792| ValueCategory = prvalue(load)
13834+
# 1792| getExpr(): [AddExpr] ... + ...
13835+
# 1792| Type = [IntType] int
13836+
# 1792| ValueCategory = prvalue
13837+
# 1792| getLeftOperand(): [VariableAccess] x
13838+
# 1792| Type = [IntType] int
13839+
# 1792| ValueCategory = prvalue(load)
13840+
# 1792| getRightOperand(): [Literal] 1
13841+
# 1792| Type = [IntType] int
13842+
# 1792| Value = [Literal] 1
13843+
# 1792| ValueCategory = prvalue
13844+
# 1792| getStmt(): [BlockStmt] { ... }
13845+
# 1793| getStmt(0): [SwitchCase] default:
13846+
# 1794| getStmt(1): [ExprStmt] ExprStmt
13847+
# 1794| getExpr(): [AssignExpr] ... = ...
13848+
# 1794| Type = [IntType] int
13849+
# 1794| ValueCategory = lvalue
13850+
# 1794| getLValue(): [VariableAccess] x
13851+
# 1794| Type = [IntType] int
13852+
# 1794| ValueCategory = lvalue
13853+
# 1794| getRValue(): [AddExpr] ... + ...
13854+
# 1794| Type = [IntType] int
13855+
# 1794| ValueCategory = prvalue
13856+
# 1794| getLeftOperand(): [VariableAccess] x
13857+
# 1794| Type = [IntType] int
13858+
# 1794| ValueCategory = prvalue(load)
13859+
# 1794| getRightOperand(): [VariableAccess] w
13860+
# 1794| Type = [IntType] int
13861+
# 1794| ValueCategory = prvalue(load)
13862+
# 1797| getStmt(3): [SwitchStmt] switch (...) ...
13863+
# 1797| getInitialization(): [ExprStmt] ExprStmt
13864+
# 1797| getExpr(): [AssignExpr] ... = ...
13865+
# 1797| Type = [IntType] int
13866+
# 1797| ValueCategory = lvalue
13867+
# 1797| getLValue(): [VariableAccess] w
13868+
# 1797| Type = [IntType] int
13869+
# 1797| ValueCategory = lvalue
13870+
# 1797| getRValue(): [VariableAccess] x
13871+
# 1797| Type = [IntType] int
13872+
# 1797| ValueCategory = prvalue(load)
13873+
# 1797| getExpr(): [ConditionDeclExpr] (condition decl)
13874+
# 1797| Type = [IntType] int
13875+
# 1797| ValueCategory = prvalue
13876+
# 1797| getVariableAccess(): [VariableAccess] w2
13877+
# 1797| Type = [IntType] int
13878+
# 1797| ValueCategory = prvalue(load)
13879+
# 1797| getStmt(): [BlockStmt] { ... }
13880+
# 1798| getStmt(0): [SwitchCase] default:
13881+
# 1799| getStmt(1): [ExprStmt] ExprStmt
13882+
# 1799| getExpr(): [AssignExpr] ... = ...
13883+
# 1799| Type = [IntType] int
13884+
# 1799| ValueCategory = lvalue
13885+
# 1799| getLValue(): [VariableAccess] x
13886+
# 1799| Type = [IntType] int
13887+
# 1799| ValueCategory = lvalue
13888+
# 1799| getRValue(): [AddExpr] ... + ...
13889+
# 1799| Type = [IntType] int
13890+
# 1799| ValueCategory = prvalue
13891+
# 1799| getLeftOperand(): [VariableAccess] x
13892+
# 1799| Type = [IntType] int
13893+
# 1799| ValueCategory = prvalue(load)
13894+
# 1799| getRightOperand(): [VariableAccess] w
13895+
# 1799| Type = [IntType] int
13896+
# 1799| ValueCategory = prvalue(load)
13897+
# 1802| getStmt(4): [SwitchStmt] switch (...) ...
13898+
# 1802| getInitialization(): [DeclStmt] declaration
13899+
# 1802| getDeclarationEntry(0): [VariableDeclarationEntry] definition of v
13900+
# 1802| Type = [IntType] int
13901+
# 1802| getVariable().getInitializer(): [Initializer] initializer for v
13902+
# 1802| getExpr(): [VariableAccess] x
13903+
# 1802| Type = [IntType] int
13904+
# 1802| ValueCategory = prvalue(load)
13905+
# 1802| getExpr(): [ConditionDeclExpr] (condition decl)
13906+
# 1802| Type = [IntType] int
13907+
# 1802| ValueCategory = prvalue
13908+
# 1802| getVariableAccess(): [VariableAccess] v2
13909+
# 1802| Type = [IntType] int
13910+
# 1802| ValueCategory = prvalue(load)
13911+
# 1802| getStmt(): [BlockStmt] { ... }
13912+
# 1803| getStmt(0): [SwitchCase] default:
13913+
# 1804| getStmt(1): [ExprStmt] ExprStmt
13914+
# 1804| getExpr(): [AssignExpr] ... = ...
13915+
# 1804| Type = [IntType] int
13916+
# 1804| ValueCategory = lvalue
13917+
# 1804| getLValue(): [VariableAccess] x
13918+
# 1804| Type = [IntType] int
13919+
# 1804| ValueCategory = lvalue
13920+
# 1804| getRValue(): [AddExpr] ... + ...
13921+
# 1804| Type = [IntType] int
13922+
# 1804| ValueCategory = prvalue
13923+
# 1804| getLeftOperand(): [VariableAccess] x
13924+
# 1804| Type = [IntType] int
13925+
# 1804| ValueCategory = prvalue(load)
13926+
# 1804| getRightOperand(): [VariableAccess] v
13927+
# 1804| Type = [IntType] int
13928+
# 1804| ValueCategory = prvalue(load)
13929+
# 1807| getStmt(5): [DeclStmt] declaration
13930+
# 1807| getDeclarationEntry(0): [VariableDeclarationEntry] definition of z
13931+
# 1807| Type = [IntType] int
13932+
# 1807| getVariable().getInitializer(): [Initializer] initializer for z
13933+
# 1807| getExpr(): [VariableAccess] x
13934+
# 1807| Type = [IntType] int
13935+
# 1807| ValueCategory = prvalue(load)
13936+
# 1808| getStmt(6): [SwitchStmt] switch (...) ...
13937+
# 1808| getExpr(): [VariableAccess] z
13938+
# 1808| Type = [IntType] int
13939+
# 1808| ValueCategory = prvalue(load)
13940+
# 1808| getStmt(): [BlockStmt] { ... }
13941+
# 1809| getStmt(0): [SwitchCase] default:
13942+
# 1810| getStmt(1): [ExprStmt] ExprStmt
13943+
# 1810| getExpr(): [AssignExpr] ... = ...
13944+
# 1810| Type = [IntType] int
13945+
# 1810| ValueCategory = lvalue
13946+
# 1810| getLValue(): [VariableAccess] x
13947+
# 1810| Type = [IntType] int
13948+
# 1810| ValueCategory = lvalue
13949+
# 1810| getRValue(): [AddExpr] ... + ...
13950+
# 1810| Type = [IntType] int
13951+
# 1810| ValueCategory = prvalue
13952+
# 1810| getLeftOperand(): [VariableAccess] x
13953+
# 1810| Type = [IntType] int
13954+
# 1810| ValueCategory = prvalue(load)
13955+
# 1810| getRightOperand(): [VariableAccess] z
13956+
# 1810| Type = [IntType] int
13957+
# 1810| ValueCategory = prvalue(load)
13958+
# 1813| getStmt(7): [SwitchStmt] switch (...) ...
13959+
# 1813| getExpr(): [ConditionDeclExpr] (condition decl)
13960+
# 1813| Type = [IntType] int
13961+
# 1813| ValueCategory = prvalue
13962+
# 1813| getVariableAccess(): [VariableAccess] z2
13963+
# 1813| Type = [IntType] int
13964+
# 1813| ValueCategory = prvalue(load)
13965+
# 1813| getStmt(): [BlockStmt] { ... }
13966+
# 1814| getStmt(0): [SwitchCase] default:
13967+
# 1815| getStmt(1): [ExprStmt] ExprStmt
13968+
# 1815| getExpr(): [AssignAddExpr] ... += ...
13969+
# 1815| Type = [IntType] int
13970+
# 1815| ValueCategory = lvalue
13971+
# 1815| getLValue(): [VariableAccess] x
13972+
# 1815| Type = [IntType] int
13973+
# 1815| ValueCategory = lvalue
13974+
# 1815| getRValue(): [VariableAccess] z2
13975+
# 1815| Type = [IntType] int
13976+
# 1815| ValueCategory = prvalue(load)
13977+
# 1817| getStmt(8): [ReturnStmt] return ...
1377913978
perf-regression.cpp:
1378013979
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
1378113980
# 4| <params>:

cpp/ql/test/library-tests/ir/ir/ir.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1782,4 +1782,38 @@ void if_initialization(int x) {
17821782
}
17831783
}
17841784

1785+
void switch_initialization(int x) {
1786+
switch (int y = x; x + 1) {
1787+
default:
1788+
x = x + y;
1789+
}
1790+
1791+
int w;
1792+
switch (w = x; x + 1) {
1793+
default:
1794+
x = x + w;
1795+
}
1796+
1797+
switch (w = x; int w2 = w) {
1798+
default:
1799+
x = x + w;
1800+
}
1801+
1802+
switch (int v = x; int v2 = v) {
1803+
default:
1804+
x = x + v;
1805+
}
1806+
1807+
int z = x;
1808+
switch (z) {
1809+
default:
1810+
x = x + z;
1811+
}
1812+
1813+
switch (int z2 = z) {
1814+
default:
1815+
x += z2;
1816+
}
1817+
}
1818+
17851819
// semmle-extractor-options: -std=c++17 --clang

0 commit comments

Comments
 (0)