Skip to content

Commit fa55e31

Browse files
authored
Merge pull request #362 from jbj/return-this-noreturn
C++: Fix "Overloaded assignment does not return 'this'" for non-returning functions
2 parents cbc2d9e + 5cbfdd1 commit fa55e31

File tree

2 files changed

+74
-5
lines changed

2 files changed

+74
-5
lines changed

cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ predicate pointerThis(Expr e) {
2525
// `f(...)`
2626
// (includes `this = ...`, where `=` is overloaded so a `FunctionCall`)
2727
exists(FunctionCall fc | fc = e and callOnThis(fc) |
28-
exists(fc.getTarget().getBlock()) implies returnsPointerThis(fc.getTarget())
28+
returnsPointerThis(fc.getTarget())
2929
) or
3030

3131
// `this = ...` (where `=` is not overloaded, so an `AssignExpr`)
@@ -38,22 +38,33 @@ predicate dereferenceThis(Expr e) {
3838
// `f(...)`
3939
// (includes `*this = ...`, where `=` is overloaded so a `FunctionCall`)
4040
exists(FunctionCall fc | fc = e and callOnThis(fc) |
41-
exists(fc.getTarget().getBlock()) implies returnsDereferenceThis(fc.getTarget())
41+
returnsDereferenceThis(fc.getTarget())
4242
) or
4343

4444
// `*this = ...` (where `=` is not overloaded, so an `AssignExpr`)
4545
dereferenceThis(e.(AssignExpr).getLValue())
4646
}
4747

48+
/**
49+
* Holds if all `return` statements in `f` return `this`, possibly indirectly.
50+
* This includes functions whose body is not in the database.
51+
*/
4852
predicate returnsPointerThis(Function f) {
49-
forex(ReturnStmt s | s.getEnclosingFunction() = f |
53+
f.getType().getUnspecifiedType() instanceof PointerType and
54+
forall(ReturnStmt s | s.getEnclosingFunction() = f and reachable(s) |
5055
// `return this`
5156
pointerThis(s.getExpr())
5257
)
5358
}
5459

60+
/**
61+
* Holds if all `return` statements in `f` return a reference to `*this`,
62+
* possibly indirectly. This includes functions whose body is not in the
63+
* database.
64+
*/
5565
predicate returnsDereferenceThis(Function f) {
56-
forex(ReturnStmt s | s.getEnclosingFunction() = f |
66+
f.getType().getUnspecifiedType() instanceof ReferenceType and
67+
forall(ReturnStmt s | s.getEnclosingFunction() = f and reachable(s) |
5768
// `return *this`
5869
dereferenceThis(s.getExpr())
5970
)
@@ -72,7 +83,6 @@ predicate assignOperatorWithWrongType(Operator op, string msg) {
7283
predicate assignOperatorWithWrongResult(Operator op, string msg) {
7384
op.hasName("operator=")
7485
and not returnsDereferenceThis(op)
75-
and exists(op.getBlock())
7686
and not op.getType() instanceof VoidType
7787
and not assignOperatorWithWrongType(op, _)
7888
and msg = "Assignment operator in class " + op.getDeclaringType().getName() + " does not return a reference to *this."

cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 82/AV Rule 82.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,65 @@ class Obj2 {
112112
int val;
113113
};
114114

115+
struct Exception {
116+
virtual ~Exception();
117+
};
118+
119+
class AlwaysThrows {
120+
public:
121+
AlwaysThrows &operator=(int _val) { // GOOD (always throws)
122+
throw Exception();
123+
// No `return` statement is generated by the C++ front end because it can
124+
// statically see that the end of the function is unreachable.
125+
}
126+
127+
AlwaysThrows &operator=(int *_val) { // GOOD (always throws)
128+
int one = 1;
129+
if (one)
130+
throw Exception();
131+
// A `return` statement is generated by the C++ front end, but the
132+
// control-flow pruning in QL will establish that this is unreachable.
133+
}
134+
};
135+
136+
class Reachability {
137+
Reachability &operator=(Reachability &that) { // GOOD
138+
int one = 1;
139+
if (one)
140+
return *this;
141+
else
142+
return that; // unreachable
143+
}
144+
145+
// helper function that always returns a reference to `*this`.
146+
Reachability &returnThisReference() {
147+
int one = 1;
148+
if (one)
149+
return *this;
150+
else
151+
return staticInstance; // unreachable
152+
}
153+
154+
// helper function that always returns `this`.
155+
Reachability *const returnThisPointer() {
156+
int one = 1;
157+
if (one)
158+
return this;
159+
else
160+
return &staticInstance; // unreachable
161+
}
162+
163+
Reachability &operator=(int _val) { // GOOD
164+
return returnThisReference();
165+
}
166+
167+
Reachability &operator=(short _val) { // GOOD
168+
return *returnThisPointer();
169+
}
170+
171+
static Reachability staticInstance;
172+
};
173+
115174
int main() {
116175
Container c;
117176
c = c;

0 commit comments

Comments
 (0)