mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-03-04 16:41:43 +00:00

Calling a pure virtual method during construction or destruction is undefined behavior. It's worth it to warn about it by default. That part is now known as the cplusplus.PureVirtualCall checker. Calling a normal virtual method during construction or destruction may be fine, but does behave unexpectedly, as it skips virtual dispatch. Do not warn about this by default, but let projects opt in into it by enabling the optin.cplusplus.VirtualCall checker manually. Give the two parts differentiated warning text: Before: Call to virtual function during construction or destruction: Call to pure virtual function during construction Call to virtual function during construction or destruction: Call to virtual function during destruction After: Pure virtual method call: Call to pure virtual method 'X::foo' during construction has undefined behavior Unexpected loss of virtual dispatch: Call to virtual method 'Y::bar' during construction bypasses virtual dispatch Also fix checker names in consumers that support them (eg., clang-tidy) because we now have different checker names for pure virtual calls and regular virtual calls. Also fix capitalization in the bug category. Differential Revision: https://reviews.llvm.org/D64274 llvm-svn: 369449
224 lines
4.6 KiB
C++
224 lines
4.6 KiB
C++
// RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.cplusplus.VirtualCall \
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
// RUN: -std=c++11 -verify=impure %s
|
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.PureVirtualCall \
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
// RUN: -std=c++11 -verify=pure -std=c++11 %s
|
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.cplusplus.VirtualCall \
|
|
// RUN: -analyzer-config \
|
|
// RUN: optin.cplusplus.VirtualCall:PureOnly=true \
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
// RUN: -std=c++11 -verify=none %s
|
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.PureVirtualCall \
|
|
// RUN: -analyzer-checker=optin.cplusplus.VirtualCall \
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
// RUN: -std=c++11 -verify=pure,impure -std=c++11 %s
|
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.PureVirtualCall \
|
|
// RUN: -analyzer-checker=optin.cplusplus.VirtualCall \
|
|
// RUN: -analyzer-config \
|
|
// RUN: optin.cplusplus.VirtualCall:PureOnly=true \
|
|
// RUN: -analyzer-checker=debug.ExprInspection \
|
|
// RUN: -std=c++11 -verify=pure %s
|
|
|
|
|
|
// We expect no diagnostics when all checks are disabled.
|
|
// none-no-diagnostics
|
|
|
|
|
|
#include "virtualcall.h"
|
|
|
|
void clang_analyzer_warnIfReached();
|
|
|
|
class A {
|
|
public:
|
|
A();
|
|
|
|
~A(){};
|
|
|
|
virtual int foo() = 0;
|
|
virtual void bar() = 0;
|
|
void f() {
|
|
foo(); // pure-warning{{Call to pure virtual method 'A::foo' during construction has undefined behavior}}
|
|
clang_analyzer_warnIfReached(); // no-warning
|
|
}
|
|
};
|
|
|
|
A::A() {
|
|
f();
|
|
}
|
|
|
|
class B {
|
|
public:
|
|
B() {
|
|
foo(); // impure-warning {{Call to virtual method 'B::foo' during construction bypasses virtual dispatch}}
|
|
}
|
|
~B();
|
|
|
|
virtual int foo();
|
|
virtual void bar() {
|
|
foo(); // impure-warning {{Call to virtual method 'B::foo' during destruction bypasses virtual dispatch}}
|
|
}
|
|
};
|
|
|
|
B::~B() {
|
|
this->B::foo(); // no-warning
|
|
this->B::bar();
|
|
this->foo(); // impure-warning {{Call to virtual method 'B::foo' during destruction bypasses virtual dispatch}}
|
|
}
|
|
|
|
class C : public B {
|
|
public:
|
|
C();
|
|
~C();
|
|
|
|
virtual int foo();
|
|
void f(int i);
|
|
};
|
|
|
|
C::C() {
|
|
f(foo()); // impure-warning {{Call to virtual method 'C::foo' during construction bypasses virtual dispatch}}
|
|
}
|
|
|
|
class D : public B {
|
|
public:
|
|
D() {
|
|
foo(); // no-warning
|
|
}
|
|
~D() { bar(); }
|
|
int foo() final;
|
|
void bar() final { foo(); } // no-warning
|
|
};
|
|
|
|
class E final : public B {
|
|
public:
|
|
E() {
|
|
foo(); // no-warning
|
|
}
|
|
~E() { bar(); }
|
|
int foo() override;
|
|
};
|
|
|
|
class F {
|
|
public:
|
|
F() {
|
|
void (F::*ptr)() = &F::foo;
|
|
(this->*ptr)();
|
|
}
|
|
void foo();
|
|
};
|
|
|
|
class G {
|
|
public:
|
|
G() {}
|
|
virtual void bar();
|
|
void foo() {
|
|
bar(); // no warning
|
|
}
|
|
};
|
|
|
|
class H {
|
|
public:
|
|
H() : initState(0) { init(); }
|
|
int initState;
|
|
virtual void f() const;
|
|
void init() {
|
|
if (initState)
|
|
f(); // no warning
|
|
}
|
|
|
|
H(int i) {
|
|
G g;
|
|
g.foo();
|
|
g.bar(); // no warning
|
|
f(); // impure-warning {{Call to virtual method 'H::f' during construction bypasses virtual dispatch}}
|
|
H &h = *this;
|
|
h.f(); // impure-warning {{Call to virtual method 'H::f' during construction bypasses virtual dispatch}}
|
|
}
|
|
};
|
|
|
|
class X {
|
|
public:
|
|
X() {
|
|
g(); // impure-warning {{Call to virtual method 'X::g' during construction bypasses virtual dispatch}}
|
|
}
|
|
X(int i) {
|
|
if (i > 0) {
|
|
X x(i - 1);
|
|
x.g(); // no warning
|
|
}
|
|
g(); // impure-warning {{Call to virtual method 'X::g' during construction bypasses virtual dispatch}}
|
|
}
|
|
virtual void g();
|
|
};
|
|
|
|
class M;
|
|
class N {
|
|
public:
|
|
virtual void virtualMethod();
|
|
void callFooOfM(M *);
|
|
};
|
|
class M {
|
|
public:
|
|
M() {
|
|
N n;
|
|
n.virtualMethod(); // no warning
|
|
n.callFooOfM(this);
|
|
}
|
|
virtual void foo();
|
|
};
|
|
void N::callFooOfM(M *m) {
|
|
m->foo(); // impure-warning {{Call to virtual method 'M::foo' during construction bypasses virtual dispatch}}
|
|
}
|
|
|
|
class Y {
|
|
public:
|
|
virtual void foobar();
|
|
void fooY() {
|
|
F f1;
|
|
foobar(); // impure-warning {{Call to virtual method 'Y::foobar' during construction bypasses virtual dispatch}}
|
|
}
|
|
Y() { fooY(); }
|
|
};
|
|
|
|
int main() {
|
|
B b;
|
|
C c;
|
|
D d;
|
|
E e;
|
|
F f;
|
|
G g;
|
|
H h;
|
|
H h1(1);
|
|
X x;
|
|
X x1(1);
|
|
M m;
|
|
Y *y = new Y;
|
|
delete y;
|
|
header::Z z;
|
|
}
|
|
|
|
namespace PR34451 {
|
|
struct a {
|
|
void b() {
|
|
a c[1];
|
|
c->b();
|
|
}
|
|
};
|
|
|
|
class e {
|
|
public:
|
|
void b() const;
|
|
};
|
|
|
|
class c {
|
|
void m_fn2() const;
|
|
e d[];
|
|
};
|
|
|
|
void c::m_fn2() const { d->b(); }
|
|
}
|