Index: clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -40,7 +40,9 @@ namespace { class VirtualCallChecker : public Checker { - mutable std::unique_ptr BT; + BugType BT{this, + "Call to virtual function during construction or destruction", + "C++ Object Lifecycle"}; public: // The flag to determine if pure virtual functions should be issued only. @@ -109,8 +111,10 @@ ProgramStateRef State = C.getState(); const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); - if (IsPureOnly && !MD->isPure()) + bool IsPure = MD->isPure(); + if (IsPureOnly && !IsPure) return; + if (!isVirtualCall(CE)) return; @@ -118,29 +122,27 @@ const ObjectState *ObState = State->get(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); - } + // At this point we're sure that we're calling a virtual function + // during construction or destruction, so we'll emit a report. + SmallString<128> Msg; + llvm::raw_svector_ostream OS(Msg); + OS << "Call to "; + if (IsPure) + OS << "pure "; + OS << "virtual function '" << MD->getQualifiedNameAsString() << "' during "; + if (*ObState == ObjectState::CtorCalled) + OS << "construction"; + else + OS << "destruction"; + + ExplodedNode *N = + IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode(); + if (!N) + return; + + auto Report = llvm::make_unique(BT, OS.str(), N); + C.emitReport(std::move(Report)); } void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction, @@ -185,21 +187,7 @@ 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(*BT, Msg, N); - C.emitReport(std::move(Reporter)); } void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) { Index: clang/test/Analysis/virtualcall.h =================================================================== --- clang/test/Analysis/virtualcall.h +++ clang/test/Analysis/virtualcall.h @@ -2,7 +2,7 @@ class Z { public: Z() { - foo(); // impure-warning {{Call to virtual function during construction}} + foo(); // impure-warning {{Call to virtual function 'header::Z::foo' during construction}} } virtual int foo(); }; Index: clang/test/Analysis/virtualcall.cpp =================================================================== --- clang/test/Analysis/virtualcall.cpp +++ clang/test/Analysis/virtualcall.cpp @@ -1,26 +1,33 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.cplusplus.VirtualCall \ +// RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -std=c++11 -verify=expected,impure %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.PureVirtualCall \ +// RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -std=c++11 -verify=expected -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=expected %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=expected,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=expected %s #include "virtualcall.h" +void clang_analyzer_warnIfReached(); + class A { public: A(); @@ -30,31 +37,32 @@ virtual int foo() = 0; virtual void bar() = 0; void f() { - foo(); // expected-warning{{Call to pure virtual function during construction}} + foo(); // expected-warning{{Call to pure virtual function 'A::foo' during construction}} + clang_analyzer_warnIfReached(); // no-warning } }; -class B : public A { +A::A() { + f(); +} + +class B { public: B() { - foo(); // impure-warning {{Call to virtual function during construction}} + foo(); // impure-warning {{Call to virtual function 'B::foo' during construction}} } ~B(); virtual int foo(); virtual void bar() { - foo(); // impure-warning{{Call to virtual function during destruction}} + foo(); // impure-warning {{Call to virtual function 'B::foo' during destruction}} } }; -A::A() { - f(); -} - B::~B() { this->B::foo(); // no-warning this->B::bar(); - this->foo(); // impure-warning {{Call to virtual function during destruction}} + this->foo(); // impure-warning {{Call to virtual function 'B::foo' during destruction}} } class C : public B { @@ -67,7 +75,7 @@ }; C::C() { - f(foo()); // impure-warning {{Call to virtual function during construction}} + f(foo()); // impure-warning {{Call to virtual function 'C::foo' during construction}} } class D : public B { @@ -121,23 +129,23 @@ G g; g.foo(); g.bar(); // no warning - f(); // impure-warning {{Call to virtual function during construction}} + f(); // impure-warning {{Call to virtual function 'H::f' during construction}} H &h = *this; - h.f(); // impure-warning {{Call to virtual function during construction}} + h.f(); // impure-warning {{Call to virtual function 'H::f' during construction}} } }; class X { public: X() { - g(); // impure-warning {{Call to virtual function during construction}} + g(); // impure-warning {{Call to virtual function 'X::g' during construction}} } X(int i) { if (i > 0) { X x(i - 1); x.g(); // no warning } - g(); // impure-warning {{Call to virtual function during construction}} + g(); // impure-warning {{Call to virtual function 'X::g' during construction}} } virtual void g(); }; @@ -158,7 +166,7 @@ virtual void foo(); }; void N::callFooOfM(M *m) { - m->foo(); // impure-warning {{Call to virtual function during construction}} + m->foo(); // impure-warning {{Call to virtual function 'M::foo' during construction}} } class Y { @@ -166,7 +174,7 @@ virtual void foobar(); void fooY() { F f1; - foobar(); // impure-warning {{Call to virtual function during construction}} + foobar(); // impure-warning {{Call to virtual function 'Y::foobar' during construction}} } Y() { fooY(); } };