[analyzer][GSoC] Re-implemente current virtual calls checker in a path-sensitive way

Patch by: Xin Wang

Differential Revision: https://reviews.llvm.org/D34275


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@311877 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Gabor Horvath 2017-08-28 08:44:43 +00:00
parent 86736b95f3
commit f068b5f616
3 changed files with 447 additions and 344 deletions

View File

@ -14,279 +14,272 @@
#include "ClangSACheckers.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
using namespace clang;
using namespace ento;
namespace {
enum class ObjectState : bool { CtorCalled, DtorCalled };
} // end namespace
// FIXME: Ascending over StackFrameContext maybe another method.
class WalkAST : public StmtVisitor<WalkAST> {
const CheckerBase *Checker;
BugReporter &BR;
AnalysisDeclContext *AC;
/// The root constructor or destructor whose callees are being analyzed.
const CXXMethodDecl *RootMethod = nullptr;
/// Whether the checker should walk into bodies of called functions.
/// Controlled by the "Interprocedural" analyzer-config option.
bool IsInterprocedural = false;
/// Whether the checker should only warn for calls to pure virtual functions
/// (which is undefined behavior) or for all virtual functions (which may
/// may result in unexpected behavior).
bool ReportPureOnly = false;
typedef const CallExpr * WorkListUnit;
typedef SmallVector<WorkListUnit, 20> DFSWorkList;
/// A vector representing the worklist which has a chain of CallExprs.
DFSWorkList WList;
// PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
// body has not been visited yet.
// PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
// body has been visited.
enum Kind { NotVisited,
PreVisited, /**< A CallExpr to this FunctionDecl is in the
worklist, but the body has not yet been
visited. */
PostVisited /**< A CallExpr to this FunctionDecl is in the
worklist, and the body has been visited. */
};
/// A DenseMap that records visited states of FunctionDecls.
llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
/// The CallExpr whose body is currently being visited. This is used for
/// generating bug reports. This is null while visiting the body of a
/// constructor or destructor.
const CallExpr *visitingCallExpr;
public:
WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac,
const CXXMethodDecl *rootMethod, bool isInterprocedural,
bool reportPureOnly)
: Checker(checker), BR(br), AC(ac), RootMethod(rootMethod),
IsInterprocedural(isInterprocedural), ReportPureOnly(reportPureOnly),
visitingCallExpr(nullptr) {
// Walking should always start from either a constructor or a destructor.
assert(isa<CXXConstructorDecl>(rootMethod) ||
isa<CXXDestructorDecl>(rootMethod));
namespace llvm {
template <> struct FoldingSetTrait<ObjectState> {
static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
ID.AddInteger(static_cast<int>(X));
}
bool hasWork() const { return !WList.empty(); }
/// This method adds a CallExpr to the worklist and marks the callee as
/// being PreVisited.
void Enqueue(WorkListUnit WLUnit) {
const FunctionDecl *FD = WLUnit->getDirectCallee();
if (!FD || !FD->getBody())
return;
Kind &K = VisitedFunctions[FD];
if (K != NotVisited)
return;
K = PreVisited;
WList.push_back(WLUnit);
}
/// This method returns an item from the worklist without removing it.
WorkListUnit Dequeue() {
assert(!WList.empty());
return WList.back();
}
void Execute() {
while (hasWork()) {
WorkListUnit WLUnit = Dequeue();
const FunctionDecl *FD = WLUnit->getDirectCallee();
assert(FD && FD->getBody());
if (VisitedFunctions[FD] == PreVisited) {
// If the callee is PreVisited, walk its body.
// Visit the body.
SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
Visit(FD->getBody());
// Mark the function as being PostVisited to indicate we have
// scanned the body.
VisitedFunctions[FD] = PostVisited;
continue;
}
// Otherwise, the callee is PostVisited.
// Remove it from the worklist.
assert(VisitedFunctions[FD] == PostVisited);
WList.pop_back();
}
}
// Stmt visitor methods.
void VisitCallExpr(CallExpr *CE);
void VisitCXXMemberCallExpr(CallExpr *CE);
void VisitStmt(Stmt *S) { VisitChildren(S); }
void VisitChildren(Stmt *S);
void ReportVirtualCall(const CallExpr *CE, bool isPure);
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// AST walking.
//===----------------------------------------------------------------------===//
void WalkAST::VisitChildren(Stmt *S) {
for (Stmt *Child : S->children())
if (Child)
Visit(Child);
}
void WalkAST::VisitCallExpr(CallExpr *CE) {
VisitChildren(CE);
if (IsInterprocedural)
Enqueue(CE);
}
void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
VisitChildren(CE);
bool callIsNonVirtual = false;
// Several situations to elide for checking.
if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
// If the member access is fully qualified (i.e., X::F), then treat
// this as a non-virtual call and do not warn.
if (CME->getQualifier())
callIsNonVirtual = true;
if (Expr *base = CME->getBase()->IgnoreImpCasts()) {
// Elide analyzing the call entirely if the base pointer is not 'this'.
if (!isa<CXXThisExpr>(base))
return;
// If the most derived class is marked final, we know that now subclass
// can override this member.
if (base->getBestDynamicClassType()->hasAttr<FinalAttr>())
callIsNonVirtual = true;
}
}
// Get the callee.
const CXXMethodDecl *MD =
dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
!MD->getParent()->hasAttr<FinalAttr>())
ReportVirtualCall(CE, MD->isPure());
if (IsInterprocedural)
Enqueue(CE);
}
void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
if (ReportPureOnly && !isPure)
return;
SmallString<100> buf;
llvm::raw_svector_ostream os(buf);
// FIXME: The interprocedural diagnostic experience here is not good.
// Ultimately this checker should be re-written to be path sensitive.
// For now, only diagnose intraprocedurally, by default.
if (IsInterprocedural) {
os << "Call Path : ";
// Name of current visiting CallExpr.
os << *CE->getDirectCallee();
// Name of the CallExpr whose body is current being walked.
if (visitingCallExpr)
os << " <-- " << *visitingCallExpr->getDirectCallee();
// Names of FunctionDecls in worklist with state PostVisited.
for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(),
E = WList.begin(); I != E; --I) {
const FunctionDecl *FD = (*(I-1))->getDirectCallee();
assert(FD);
if (VisitedFunctions[FD] == PostVisited)
os << " <-- " << *FD;
}
os << "\n";
}
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
SourceRange R = CE->getCallee()->getSourceRange();
os << "Call to ";
if (isPure)
os << "pure ";
os << "virtual function during ";
if (isa<CXXConstructorDecl>(RootMethod))
os << "construction ";
else
os << "destruction ";
if (isPure)
os << "has undefined behavior";
else
os << "will not dispatch to derived class";
BR.EmitBasicReport(AC->getDecl(), Checker,
"Call to virtual function during construction or "
"destruction",
"C++ Object Lifecycle", os.str(), CELoc, R);
}
//===----------------------------------------------------------------------===//
// VirtualCallChecker
//===----------------------------------------------------------------------===//
} // end namespace llvm
namespace {
class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
class VirtualCallChecker
: public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
mutable std::unique_ptr<BugType> BT;
public:
DefaultBool isInterprocedural;
DefaultBool isPureOnly;
// The flag to determine if pure virtual functions should be issued only.
DefaultBool IsPureOnly;
void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
BugReporter &BR) const {
AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(RD);
void checkBeginFunction(CheckerContext &C) const;
void checkEndFunction(CheckerContext &C) const;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
// Check the constructors.
for (const auto *I : RD->ctors()) {
if (!I->isCopyOrMoveConstructor())
if (Stmt *Body = I->getBody()) {
WalkAST walker(this, BR, ADC, I, isInterprocedural, isPureOnly);
walker.Visit(Body);
walker.Execute();
}
private:
void registerCtorDtorCallInState(bool IsBeginFunction,
CheckerContext &C) const;
void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
CheckerContext &C) const;
class VirtualBugVisitor : public BugReporterVisitorImpl<VirtualBugVisitor> {
private:
const MemRegion *ObjectRegion;
bool Found;
public:
VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int X = 0;
ID.AddPointer(&X);
ID.AddPointer(ObjectRegion);
}
// Check the destructor.
if (CXXDestructorDecl *DD = RD->getDestructor())
if (Stmt *Body = DD->getBody()) {
WalkAST walker(this, BR, ADC, DD, isInterprocedural, isPureOnly);
walker.Visit(Body);
walker.Execute();
}
}
std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
BugReport &BR) override;
};
};
} // end namespace
// GDM (generic data map) to the memregion of this for the ctor and dtor.
REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
std::shared_ptr<PathDiagnosticPiece>
VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
BugReport &BR) {
// We need the last ctor/dtor which call the virtual function.
// The visitor walks the ExplodedGraph backwards.
if (Found)
return nullptr;
ProgramStateRef State = N->getState();
const LocationContext *LCtx = N->getLocationContext();
const CXXConstructorDecl *CD =
dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl());
const CXXDestructorDecl *DD =
dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl());
if (!CD && !DD)
return nullptr;
ProgramStateManager &PSM = State->getStateManager();
auto &SVB = PSM.getSValBuilder();
const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
if (!MD)
return nullptr;
auto ThiSVal =
State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
if (!Reg)
return nullptr;
if (Reg != ObjectRegion)
return nullptr;
const Stmt *S = PathDiagnosticLocation::getStmt(N);
if (!S)
return nullptr;
Found = true;
std::string InfoText;
if (CD)
InfoText = "This constructor of an object of type '" +
CD->getNameAsString() +
"' has not returned when the virtual method was called";
else
InfoText = "This destructor of an object of type '" +
DD->getNameAsString() +
"' has not returned when the virtual method was called";
// Generate the extra diagnostic.
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
N->getLocationContext());
return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
}
// The function to check if a callexpr is a virtual function.
static bool isVirtualCall(const CallExpr *CE) {
bool CallIsNonVirtual = false;
if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
// The member access is fully qualified (i.e., X::F).
// Treat this as a non-virtual call and do not warn.
if (CME->getQualifier())
CallIsNonVirtual = true;
if (const Expr *Base = CME->getBase()->IgnoreImpCasts()) {
// The most derived class is marked final.
if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
CallIsNonVirtual = true;
}
}
const CXXMethodDecl *MD =
dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
!MD->getParent()->hasAttr<FinalAttr>())
return true;
return false;
}
// The BeginFunction callback when enter a constructor or a destructor.
void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
registerCtorDtorCallInState(true, C);
}
// The EndFunction callback when leave a constructor or a destructor.
void VirtualCallChecker::checkEndFunction(CheckerContext &C) const {
registerCtorDtorCallInState(false, C);
}
void VirtualCallChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
const auto MC = dyn_cast<CXXMemberCall>(&Call);
if (!MC)
return;
const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
if (!MD)
return;
ProgramStateRef State = C.getState();
const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (IsPureOnly && !MD->isPure())
return;
if (!isVirtualCall(CE))
return;
const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
if (!ObState)
return;
// Check if a virtual method is called.
// The GDM of constructor and destructor should be true.
if (*ObState == ObjectState::CtorCalled) {
if (IsPureOnly && MD->isPure())
reportBug("Call to pure virtual function during construction", true, Reg,
C);
else if (!MD->isPure())
reportBug("Call to virtual function during construction", false, Reg, C);
else
reportBug("Call to pure virtual function during construction", false, Reg,
C);
}
if (*ObState == ObjectState::DtorCalled) {
if (IsPureOnly && MD->isPure())
reportBug("Call to pure virtual function during destruction", true, Reg,
C);
else if (!MD->isPure())
reportBug("Call to virtual function during destruction", false, Reg, C);
else
reportBug("Call to pure virtual function during construction", false, Reg,
C);
}
}
void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
CheckerContext &C) const {
const auto *LCtx = C.getLocationContext();
const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
if (!MD)
return;
ProgramStateRef State = C.getState();
auto &SVB = C.getSValBuilder();
// Enter a constructor, set the corresponding memregion be true.
if (isa<CXXConstructorDecl>(MD)) {
auto ThiSVal =
State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
const MemRegion *Reg = ThiSVal.getAsRegion();
if (IsBeginFunction)
State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
else
State = State->remove<CtorDtorMap>(Reg);
C.addTransition(State);
return;
}
// Enter a Destructor, set the corresponding memregion be true.
if (isa<CXXDestructorDecl>(MD)) {
auto ThiSVal =
State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
const MemRegion *Reg = ThiSVal.getAsRegion();
if (IsBeginFunction)
State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
else
State = State->remove<CtorDtorMap>(Reg);
C.addTransition(State);
return;
}
}
void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
const MemRegion *Reg,
CheckerContext &C) const {
ExplodedNode *N;
if (IsSink)
N = C.generateErrorNode();
else
N = C.generateNonFatalErrorNode();
if (!N)
return;
if (!BT)
BT.reset(new BugType(
this, "Call to virtual function during construction or destruction",
"C++ Object Lifecycle"));
auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
C.emitReport(std::move(Reporter));
}
void ento::registerVirtualCallChecker(CheckerManager &mgr) {
VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
checker->isInterprocedural =
mgr.getAnalyzerOptions().getBooleanOption("Interprocedural", false,
checker);
checker->isPureOnly =
mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false,
checker);
checker->IsPureOnly =
mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, checker);
}

View File

@ -1,98 +1,83 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -verify -std=c++11 %s
// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:Interprocedural=true -DINTERPROCEDURAL=1 -verify -std=c++11 %s
// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:PureOnly=true -DPUREONLY=1 -verify -std=c++11 %s
// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-output=text -verify -std=c++11 %s
/* When INTERPROCEDURAL is set, we expect diagnostics in all functions reachable
from a constructor or destructor. If it is not set, we expect diagnostics
only in the constructor or destructor.
// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:PureOnly=true -DPUREONLY=1 -analyzer-output=text -verify -std=c++11 %s
When PUREONLY is set, we expect diagnostics only for calls to pure virtual
functions not to non-pure virtual functions.
*/
#include "virtualcall.h"
class A {
public:
A();
A(int i);
~A() {};
virtual int foo() = 0; // from Sema: expected-note {{'foo' declared here}}
~A(){};
virtual int foo() = 0;
virtual void bar() = 0;
void f() {
foo();
#if INTERPROCEDURAL
// expected-warning-re@-2 {{{{^}}Call Path : foo <-- fCall to pure virtual function during construction has undefined behavior}}
#endif
// expected-warning-re@-1 {{{{^}}Call to pure virtual function during construction}}
// expected-note-re@-2 {{{{^}}Call to pure virtual function during construction}}
}
};
class B : public A {
public:
B() {
foo();
B() { // expected-note {{Calling default constructor for 'A'}}
foo();
#if !PUREONLY
#if INTERPROCEDURAL
// expected-warning-re@-3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}}
#else
// expected-warning-re@-5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}}
// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
// expected-note-re@-3 {{{{^}}This constructor of an object of type 'B' has not returned when the virtual method was called}}
// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
#endif
}
~B();
virtual int foo();
virtual void bar() { foo(); }
#if INTERPROCEDURAL
// expected-warning-re@-2 {{{{^}}Call Path : foo <-- barCall to virtual function during destruction will not dispatch to derived class}}
virtual void bar() {
foo();
#if !PUREONLY
// expected-warning-re@-2 {{{{^}}Call to virtual function during destruction}}
// expected-note-re@-3 {{{{^}}Call to virtual function during destruction}}
#endif
}
};
A::A() {
f();
}
A::A(int i) {
foo(); // From Sema: expected-warning {{call to pure virtual member function 'foo' has undefined behavior}}
#if INTERPROCEDURAL
// expected-warning-re@-2 {{{{^}}Call Path : fooCall to pure virtual function during construction has undefined behavior}}
#else
// expected-warning-re@-4 {{{{^}}Call to pure virtual function during construction has undefined behavior}}
#endif
A::A() {
f();
// expected-note-re@-1 {{{{^}}This constructor of an object of type 'A' has not returned when the virtual method was called}}
// expected-note-re@-2 {{{{^}}Calling 'A::f'}}
}
B::~B() {
this->B::foo(); // no-warning
this->B::bar();
this->foo();
#if !PUREONLY
#if INTERPROCEDURAL
// expected-warning-re@-3 {{{{^}}Call Path : fooCall to virtual function during destruction will not dispatch to derived class}}
#else
// expected-warning-re@-5 {{{{^}}Call to virtual function during destruction will not dispatch to derived class}}
// expected-note-re@-2 {{{{^}}This destructor of an object of type '~B' has not returned when the virtual method was called}}
// expected-note-re@-3 {{{{^}}Calling 'B::bar'}}
#endif
this->foo();
#if !PUREONLY
// expected-warning-re@-2 {{{{^}}Call to virtual function during destruction}}
// expected-note-re@-3 {{{{^}}This destructor of an object of type '~B' has not returned when the virtual method was called}}
// expected-note-re@-4 {{{{^}}Call to virtual function during destruction}}
#endif
}
class C : public B {
public:
C();
~C();
virtual int foo();
void f(int i);
};
C::C() {
f(foo());
f(foo());
#if !PUREONLY
#if INTERPROCEDURAL
// expected-warning-re@-3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}}
#else
// expected-warning-re@-5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}}
#endif
// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
// expected-note-re@-3 {{{{^}}This constructor of an object of type 'C' has not returned when the virtual method was called}}
// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
}
@ -112,30 +97,177 @@ public:
foo(); // no-warning
}
~E() { bar(); }
#if !PUREONLY
// expected-note-re@-2 2{{{{^}}Calling '~B'}}
#endif
int foo() override;
};
// Regression test: don't crash when there's no direct callee.
class F {
public:
F() {
void (F::* ptr)() = &F::foo;
void (F::*ptr)() = &F::foo;
(this->*ptr)();
}
void foo();
};
int main() {
A *a;
B *b;
C *c;
D *d;
E *e;
F *f;
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();
#if !PUREONLY
// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
// expected-note-re@-3 {{{{^}}This constructor of an object of type 'H' has not returned when the virtual method was called}}
// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
H &h = *this;
h.f();
#if !PUREONLY
// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
// expected-note-re@-3 {{{{^}}This constructor of an object of type 'H' has not returned when the virtual method was called}}
// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
}
};
class X {
public:
X() {
g();
#if !PUREONLY
// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
// expected-note-re@-3 {{{{^}}This constructor of an object of type 'X' has not returned when the virtual method was called}}
// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
}
X(int i) {
if (i > 0) {
#if !PUREONLY
// expected-note-re@-2 {{{{^}}Taking true branch}}
// expected-note-re@-3 {{{{^}}Taking false branch}}
#endif
X x(i - 1);
#if !PUREONLY
// expected-note-re@-2 {{{{^}}Calling constructor for 'X'}}
#endif
x.g(); // no warning
}
g();
#if !PUREONLY
// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
// expected-note-re@-3 {{{{^}}This constructor of an object of type 'X' has not returned when the virtual method was called}}
// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
}
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);
#if !PUREONLY
// expected-note-re@-2 {{{{^}}This constructor of an object of type 'M' has not returned when the virtual method was called}}
// expected-note-re@-3 {{{{^}}Calling 'N::callFooOfM'}}
#endif
}
virtual void foo();
};
void N::callFooOfM(M *m) {
m->foo();
#if !PUREONLY
// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
// expected-note-re@-3 {{{{^}}Call to virtual function during construction}}
#endif
}
#include "virtualcall.h"
class Y {
public:
virtual void foobar();
void fooY() {
F f1;
foobar();
#if !PUREONLY
// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
// expected-note-re@-3 {{{{^}}Call to virtual function during construction}}
#endif
}
Y() { fooY(); }
#if !PUREONLY
// expected-note-re@-2 {{{{^}}This constructor of an object of type 'Y' has not returned when the virtual method was called}}
// expected-note-re@-3 {{{{^}}Calling 'Y::fooY'}}
#endif
};
#define AS_SYSTEM
#include "virtualcall.h"
#undef AS_SYSTEM
int main() {
B b;
#if PUREONLY
//expected-note-re@-2 {{{{^}}Calling default constructor for 'B'}}
#else
//expected-note-re@-4 2{{{{^}}Calling default constructor for 'B'}}
#endif
C c;
#if !PUREONLY
//expected-note-re@-2 {{{{^}}Calling default constructor for 'C'}}
#endif
D d;
E e;
F f;
G g;
H h;
H h1(1);
#if !PUREONLY
//expected-note-re@-2 {{{{^}}Calling constructor for 'H'}}
//expected-note-re@-3 {{{{^}}Calling constructor for 'H'}}
#endif
X x;
#if !PUREONLY
//expected-note-re@-2 {{{{^}}Calling default constructor for 'X'}}
#endif
X x1(1);
#if !PUREONLY
//expected-note-re@-2 {{{{^}}Calling constructor for 'X'}}
#endif
M m;
#if !PUREONLY
//expected-note-re@-2 {{{{^}}Calling default constructor for 'M'}}
#endif
Y *y = new Y;
delete y;
header::Z z;
#if !PUREONLY
// expected-note-re@-2 {{{{^}}Calling default constructor for 'Z'}}
#endif
}
#if !PUREONLY
//expected-note-re@-2 2{{{{^}}Calling '~E'}}
#endif

View File

@ -1,36 +1,14 @@
#ifdef AS_SYSTEM
#pragma clang system_header
namespace system {
class A {
public:
A() {
foo(); // no-warning
}
virtual int foo();
};
}
#else
namespace header {
class A {
class Z {
public:
A() {
Z() {
foo();
#if !PUREONLY
#if INTERPROCEDURAL
// expected-warning-re@-3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}}
#else
// expected-warning-re@-5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}}
// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
// expected-note-re@-3 {{{{^}}This constructor of an object of type 'Z' has not returned when the virtual method was called}}
// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
#endif
}
virtual int foo();
};
}
#endif