Skip to content

Commit bfb8125

Browse files
Merge pull request #754 from jbj/copy-assignment-no-effect
C++: Exclude assignment operator in ExprHasNoEffect
2 parents 338754f + 189d82b commit bfb8125

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.ql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,27 @@ predicate definedInIfDef(Function f) {
9191
)
9292
}
9393

94+
/**
95+
* Holds if `call` has the form `B::f()` or `q.B::f()`, where `B` is a base
96+
* class of the class containing `call`.
97+
*
98+
* This is most often used for calling base-class functions from within
99+
* overrides. Those functions may have no side effect in the current
100+
* implementation, but we should not advise callers to rely on this. That would
101+
* break encapsulation.
102+
*/
103+
predicate baseCall(FunctionCall call) {
104+
call.getNameQualifier().getQualifyingElement() =
105+
call.getEnclosingFunction().getDeclaringType().(Class).getABaseClass+()
106+
}
107+
94108
from PureExprInVoidContext peivc, Locatable parent,
95109
Locatable info, string info_text, string tail
96110
where // EQExprs are covered by CompareWhereAssignMeant.ql
97111
not peivc instanceof EQExpr and
98112
// as is operator==
99113
not peivc.(FunctionCall).getTarget().hasName("operator==") and
114+
not baseCall(peivc) and
100115
not accessInInitOfForStmt(peivc) and
101116
not peivc.isCompilerGenerated() and
102117
not exists(Macro m | peivc = m.getAnInvocation().getAnExpandedElement()) and

cpp/ql/src/semmle/code/cpp/NameQualifiers.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ class NameQualifier extends NameQualifiableElement, @namequalifier {
6767
* the latter is qualified by `N1`.
6868
*/
6969
class NameQualifiableElement extends Element, @namequalifiableelement {
70-
/** Gets the name qualifier associated with this element. */
70+
/**
71+
* Gets the name qualifier associated with this element. For example, the
72+
* name qualifier of `N::f()` is `N`.
73+
*/
7174
NameQualifier getNameQualifier() {
7275
namequalifiers(unresolveElement(result),underlyingElement(this),_,_)
7376
}

cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/test.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,45 @@ void testFunc2()
6464
MyAssignable v1, v2;
6565
v2 = v1;
6666
}
67+
68+
namespace std {
69+
typedef unsigned long size_t;
70+
}
71+
72+
void* operator new (std::size_t size, void* ptr) noexcept;
73+
74+
struct Base {
75+
Base() { }
76+
77+
Base &operator=(const Base &rhs) {
78+
return *this;
79+
}
80+
81+
virtual ~Base() { }
82+
};
83+
84+
struct Derived : Base {
85+
Derived &operator=(const Derived &rhs) {
86+
if (&rhs == this) {
87+
return *this;
88+
}
89+
90+
// In case base class has data, now or in the future, copy that first.
91+
Base::operator=(rhs); // GOOD
92+
93+
this->m_x = rhs.m_x;
94+
return *this;
95+
}
96+
97+
int m_x;
98+
};
99+
100+
void use_Base() {
101+
Base base_buffer[1];
102+
Base *base = new(&base_buffer[0]) Base();
103+
104+
// In case the destructor does something, now or in the future, call it. It
105+
// won't get called automatically because the object was allocated with
106+
// placement new.
107+
base->~Base(); // GOOD (because the call has void type)
108+
}

0 commit comments

Comments
 (0)