Index: lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -14,279 +14,428 @@ #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 { -class WalkAST : public StmtVisitor { - 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 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. */ +class VirtualCallChecker + : public Checker { + mutable std::unique_ptr BT; + +public: + // The flag to determine if pure virtual functions should be issued only. + DefaultBool IsPureOnly; + + void checkBeginFunction(CheckerContext &C) const; + void checkEndFunction(CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + bool IsCalledbyCtor(const CallExpr *CE, ProgramStateRef State, + const LocationContext *LCtx) const; + bool IsCalledbyDtor(const CallExpr *CE, ProgramStateRef State, + const LocationContext *LCtx) const; + +private: + bool IsVirtualCall(const CallExpr *CE) const; + Optional getThisSVal(const StackFrameContext *SFC, + const ProgramStateRef State) const; + + class VirtualBugVisitor : public BugReporterVisitorImpl { + private: + const unsigned TrackedCtorDtorFlag; + bool Found; + + public: + VirtualBugVisitor(const unsigned TrackedCtorDtorFlag) + : TrackedCtorDtorFlag(TrackedCtorDtorFlag), Found(false) {} + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int x = 0; + ID.AddPointer(&x); + ID.AddPointer(&TrackedCtorDtorFlag); + } + std::shared_ptr VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override; }; +}; +} - /// A DenseMap that records visited states of FunctionDecls. - llvm::DenseMap VisitedFunctions; +// GDM (generic data map) to store two integers in the program State. +// One integer for constructors, one integer for destructors. +REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstructorFlag, unsigned) +REGISTER_TRAIT_WITH_PROGRAMSTATE(DestructorFlag, unsigned) +// GDM (generic data map) to determine if a function is called by an object. +REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectFlag, unsigned) +// GDM (generic data map) to the memregion of this for the ctor and dtor. +REGISTER_MAP_WITH_PROGRAMSTATE(CtorMap, const MemRegion *, unsigned) +REGISTER_MAP_WITH_PROGRAMSTATE(DtorMap, const MemRegion *, unsigned) + +std::shared_ptr +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 unsigned Ctorflag = State->get(); + const unsigned Dtorflag = State->get(); + const LocationContext *LCtx = N->getLocationContext(); + const CXXConstructorDecl *CD = + dyn_cast_or_null(LCtx->getDecl()); + const CXXDestructorDecl *DD = + dyn_cast_or_null(LCtx->getDecl()); + + if ((!CD && !DD) || + (Ctorflag != TrackedCtorDtorFlag && Dtorflag != TrackedCtorDtorFlag)) + return nullptr; + + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + Found = true; + + std::string DeclName; + std::string InfoText; + if (CD) { + DeclName = CD->getNameAsString(); + InfoText = "Called from this constrctor " + DeclName; + } else { + DeclName = DD->getNameAsString(); + InfoText = "called from this destructor " + DeclName; + } - /// 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; + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared(Pos, InfoText, true); +} -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(rootMethod) || - isa(rootMethod)); - } - - 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); +void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const { + const auto *LCtx = C.getLocationContext(); + const auto *MD = dyn_cast(LCtx->getDecl()); + if (!MD) + return; + + ProgramStateRef State = C.getState(); + + // Enter a constructor, increase the corresponding integer + if (isa(MD)) { + unsigned Constructorflag = State->get(); + State = State->set(++Constructorflag); + C.addTransition(State); + return; } - /// 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 SaveCall(visitingCallExpr, WLUnit); - Visit(FD->getBody()); - - // Mark the function as being PostVisited to indicate we have - // scanned the body. - VisitedFunctions[FD] = PostVisited; - continue; - } + // Enter a Destructor, increase the corresponding integer + if (isa(MD)) { + unsigned Destructorflag = State->get(); + State = State->set(++Destructorflag); + C.addTransition(State); + return; + } +} - // Otherwise, the callee is PostVisited. - // Remove it from the worklist. - assert(VisitedFunctions[FD] == PostVisited); - WList.pop_back(); - } +// The PostCall callback, when leave a constructor or a destructor, +// decrease the corresponding integer +void VirtualCallChecker::checkEndFunction(CheckerContext &C) const { + const auto *LCtx = C.getLocationContext(); + const auto *MD = dyn_cast(LCtx->getDecl()); + if (!MD) + return; + + ProgramStateRef State = C.getState(); + if (isa(MD)) { + unsigned Constructorflag = State->get(); + State = State->set(--Constructorflag); + C.addTransition(State); + return; } - // Stmt visitor methods. - void VisitCallExpr(CallExpr *CE); - void VisitCXXMemberCallExpr(CallExpr *CE); - void VisitStmt(Stmt *S) { VisitChildren(S); } - void VisitChildren(Stmt *S); + if (isa(MD)) { + unsigned Destructorflag = State->get(); + State = State->set(--Destructorflag); + C.addTransition(State); + return; + } +} - void ReportVirtualCall(const CallExpr *CE, bool isPure); +void VirtualCallChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const Decl *D = Call.getDecl(); + if (!D) + return; -}; -} // end anonymous namespace + const auto *CC = dyn_cast(&Call); + const auto *CD = dyn_cast(&Call); -//===----------------------------------------------------------------------===// -// AST walking. -//===----------------------------------------------------------------------===// + ProgramStateRef State = C.getState(); + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + const LocationContext *LCtx = C.getLocationContext(); + const StackFrameContext *SFC = LCtx->getCurrentStackFrame(); + + Optional ThisSVal = getThisSVal(SFC, State); + const CXXMethodDecl *MD = dyn_cast(D); + + // Enter a constructor, increase the corresponding integer. + if (isa(D)) { + if (!CC) + return; + const MemRegion *Reg = CC->getCXXThisVal().getAsRegion(); + State = State->set(Reg, 1); + C.addTransition(State); + return; + } -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(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; + // Enter a Destructor, increase the corresponding integer. + if (isa(D)) { + if (!CD) + return; + const MemRegion *Reg = CD->getCXXThisVal().getAsRegion(); + State = State->set(Reg, 1); + C.addTransition(State); + return; + } + + // Set the Objectflag. + if (const MemberExpr *CME = dyn_cast(CE->getCallee())) { + // The member access is fully qualified (i.e., X::F). + // Treat this as a non-virtual call and do not warn. + if (const Expr *Base = CME->getBase()->IgnoreImpCasts()) { + if (!isa(Base)) { + SVal CEV = State->getSVal(Base, LCtx); + if (CEV != ThisSVal) { + unsigned Objectflag = State->get(); + State = State->set(++Objectflag); + C.addTransition(State); + } + } + } + } - if (Expr *base = CME->getBase()->IgnoreImpCasts()) { - // Elide analyzing the call entirely if the base pointer is not 'this'. - if (!isa(base)) + // Check if a virtual method is called. + // The GDM of constructor and destructor should be larger than 0. + if (IsPureOnly && !MD->isPure()) + return; + + if (IsVirtualCall(CE) && State->get() > 0 && + (State->get() == 0 || + (State->get() > 0 && IsCalledbyCtor(CE, State, LCtx)))) { + if (IsPureOnly && MD->isPure()) { + ExplodedNode *N = C.generateErrorNode(); + if (!N) return; + if (!BT) + BT.reset(new BugType(this, "Virtual Call", "Path-Sensitive")); - // If the most derived class is marked final, we know that now subclass - // can override this member. - if (base->getBestDynamicClassType()->hasAttr()) - callIsNonVirtual = true; + auto Reporter = llvm::make_unique( + *BT, "Call to pure function during construction", N); + const unsigned Ctorflag = State->get(); + Reporter->addVisitor(llvm::make_unique(Ctorflag)); + C.emitReport(std::move(Reporter)); + return; + } else { + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!BT) + BT.reset(new BugType(this, "Virtual Call", "Path-Sensitive")); + auto Reporter = llvm::make_unique( + *BT, "Call to virtual function during construction", N); + const unsigned Ctorflag = State->get(); + Reporter->addVisitor(llvm::make_unique(Ctorflag)); + C.emitReport(std::move(Reporter)); + return; } } - // Get the callee. - const CXXMethodDecl *MD = - dyn_cast_or_null(CE->getDirectCallee()); - if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr() && - !MD->getParent()->hasAttr()) - ReportVirtualCall(CE, MD->isPure()); + if (IsVirtualCall(CE) && State->get() > 0 && + (State->get() == 0 || + (State->get() > 0 && IsCalledbyDtor(CE, State, LCtx)))) { + if (IsPureOnly && MD->isPure()) { + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + if (!BT) + BT.reset(new BugType(this, "Virtual Call", "Path-Sensitive")); - if (IsInterprocedural) - Enqueue(CE); + auto Reporter = llvm::make_unique( + *BT, "Call to pure function during destruction", N); + const unsigned Dtorflag = State->get(); + Reporter->addVisitor(llvm::make_unique(Dtorflag)); + C.emitReport(std::move(Reporter)); + return; + } else { + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!BT) + BT.reset(new BugType(this, "Virtual Call", "Path-Sensitive")); + + auto Reporter = llvm::make_unique( + *BT, "Call to virtual function during destruction", N); + const unsigned Dtorflag = State->get(); + Reporter->addVisitor(llvm::make_unique(Dtorflag)); + C.emitReport(std::move(Reporter)); + return; + } + } } -void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) { - if (ReportPureOnly && !isPure) +// When leave a constructor or a destructor,decrease the corresponding integer. +void VirtualCallChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + const Decl *D = Call.getDecl(); + if (!D) 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::iterator I = WList.end(), - E = WList.begin(); I != E; --I) { - const FunctionDecl *FD = (*(I-1))->getDirectCallee(); - assert(FD); - if (VisitedFunctions[FD] == PostVisited) - os << " <-- " << *FD; - } + const auto *CC = dyn_cast(&Call); + const auto *CD = dyn_cast(&Call); - os << "\n"; + ProgramStateRef State = C.getState(); + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + const LocationContext *LCtx = C.getLocationContext(); + const StackFrameContext *SFC = LCtx->getCurrentStackFrame(); + + Optional ThisSVal = getThisSVal(SFC, State); + + if (isa(D)) { + const MemRegion *Reg = CC->getCXXThisVal().getAsRegion(); + State = State->remove(Reg); + C.addTransition(State); + return; } - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - SourceRange R = CE->getCallee()->getSourceRange(); - - os << "Call to "; - if (isPure) - os << "pure "; + if (isa(D)) { + const MemRegion *Reg = CD->getCXXThisVal().getAsRegion(); + State = State->remove(Reg); + C.addTransition(State); + return; + } - os << "virtual function during "; + if (const MemberExpr *CME = dyn_cast(CE->getCallee())) { + if (const Expr *Base = CME->getBase()->IgnoreImpCasts()) { + if (!isa(Base)) { + SVal CEV = State->getSVal(Base, LCtx); + if (CEV != ThisSVal) { + unsigned Objectflag = State->get(); + State = State->set(--Objectflag); + C.addTransition(State); + } + } + } + } +} - if (isa(RootMethod)) - os << "construction "; - else - os << "destruction "; +// The function to check if a callexpr is a virtual function. +bool VirtualCallChecker::IsVirtualCall(const CallExpr *CE) const { + bool CallIsNonVirtual = false; + + if (const MemberExpr *CME = dyn_cast(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 (isPure) - os << "has undefined behavior"; - else - os << "will not dispatch to derived class"; + if (const Expr *Base = CME->getBase()->IgnoreImpCasts()) { + // The most derived class is marked final. + if (Base->getBestDynamicClassType()->hasAttr()) + CallIsNonVirtual = true; + } + } - BR.EmitBasicReport(AC->getDecl(), Checker, - "Call to virtual function during construction or " - "destruction", - "C++ Object Lifecycle", os.str(), CELoc, R); + const CXXMethodDecl *MD = + dyn_cast_or_null(CE->getDirectCallee()); + if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr() && + !MD->getParent()->hasAttr()) + return true; + return false; } -//===----------------------------------------------------------------------===// -// VirtualCallChecker -//===----------------------------------------------------------------------===// - -namespace { -class VirtualCallChecker : public Checker > { -public: - DefaultBool isInterprocedural; - DefaultBool isPureOnly; +// Get the SVal of the "this" object for a method call in the given stackframe. +// Returns None if the stack frame does not represent a method call. +Optional +VirtualCallChecker::getThisSVal(const StackFrameContext *SFC, + const ProgramStateRef State) const { + if (SFC->inTopFrame()) { + const FunctionDecl *FD = SFC->getDecl()->getAsFunction(); + if (!FD) + return None; + const CXXMethodDecl *MD = dyn_cast(FD->getParent()); + if (!MD) + return None; + Loc ThisLoc = State->getStateManager().getSValBuilder().getCXXThis(MD, SFC); + return State->getSVal(ThisLoc); + } else { + const Stmt *S = SFC->getCallSite(); + if (!S) + return None; + if (const CXXMemberCallExpr *MCE = dyn_cast_or_null(S)) + return State->getSVal(MCE->getImplicitObjectArgument(), SFC->getParent()); + else if (const CXXConstructExpr *CCE = + dyn_cast_or_null(S)) + return State->getSVal(CCE, SFC->getParent()); + return None; + } +} - void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr, - BugReporter &BR) const { - AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(RD); - - // 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(); +// Check the base of the callexpr is equal to the this of the ctor. +bool VirtualCallChecker::IsCalledbyCtor(const CallExpr *CE, + ProgramStateRef State, + const LocationContext *LCtx) const { + if (const MemberExpr *CME = dyn_cast(CE->getCallee())) { + if (const Expr *Base = CME->getBase()->IgnoreImpCasts()) { + if (!isa(Base)) { + SVal CEV = State->getSVal(Base, LCtx); + CtorMapTy Regmap = State->get(); + for (CtorMapTy::iterator I = Regmap.begin(), E = Regmap.end(); I != E; + ++I) { + const MemRegion *Curreg = I->first; + SVal CurSV = State->getSVal(Curreg); + if (CEV == CurSV) + return true; } + } } + } + return false; +} - // 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(); +// Check the base of the callexpr is equal to the this of the dtor. +bool VirtualCallChecker::IsCalledbyDtor(const CallExpr *CE, + ProgramStateRef State, + const LocationContext *LCtx) const { + if (const MemberExpr *CME = dyn_cast(CE->getCallee())) { + if (const Expr *Base = CME->getBase()->IgnoreImpCasts()) { + if (!isa(Base)) { + SVal CEV = State->getSVal(Base, LCtx); + DtorMapTy Regmap = State->get(); + for (DtorMapTy::iterator I = Regmap.begin(), E = Regmap.end(); I != E; + ++I) { + const MemRegion *Curreg = I->first; + SVal CurSV = State->getSVal(Curreg); + if (CEV == CurSV) + return true; + } } + } } -}; + return false; } void ento::registerVirtualCallChecker(CheckerManager &mgr) { VirtualCallChecker *checker = mgr.registerChecker(); - checker->isInterprocedural = - mgr.getAnalyzerOptions().getBooleanOption("Interprocedural", false, - checker); - - checker->isPureOnly = - mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, - checker); + + checker->IsPureOnly = + mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, checker); } Index: test/Analysis/virtualcall.cpp =================================================================== --- test/Analysis/virtualcall.cpp +++ test/Analysis/virtualcall.cpp @@ -1,29 +1,18 @@ // 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 -/* 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. - - When PUREONLY is set, we expect diagnostics only for calls to pure virtual - functions not to non-pure virtual functions. -*/ +// 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 class A { public: A(); - A(int i); ~A() {}; - virtual int foo() = 0; // from Sema: expected-note {{'foo' declared here}} - virtual void bar() = 0; + 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:Call to virtual function during construction } }; @@ -31,49 +20,24 @@ public: B() { 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 -#endif - + // expected-warning:Call to virtual function during construction } ~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}} -#endif + // expected-warning:Call to virtual function during destruction }; 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 -} - 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}} -#endif -#endif - + // expected-warning:Call to virtual function during destruction } class C : public B { @@ -87,13 +51,7 @@ C::C() { 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 -#endif + // expected-warning:Call to virtual function during construction } class D : public B { @@ -103,7 +61,8 @@ } ~D() { bar(); } int foo() final; - void bar() final { foo(); } // no-warning + void bar() final { foo(); } + // no-warning }; class E final : public B { @@ -115,7 +74,6 @@ int foo() override; }; -// Regression test: don't crash when there's no direct callee. class F { public: F() { @@ -125,17 +83,96 @@ void foo(); }; -int main() { - A *a; - B *b; - C *c; - D *d; - E *e; - F *f; +class G { +public: + 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(); + // expected-warning:Call to virtual function during construction + H& h = *this; + h.f(); + // expected-warning:Call to virtual function during construction + } +}; + +class X { +public: + X() { + g(); + // expected-warning:Call to virtual function during construction + } + X(int i) { + if (i > 0) { + X x(i-1); + x.g(); + // no warning + } + g(); + // expected-warning:Call to virtual function during construction + } + 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(); + // expected-warning:Call to virtual function during construction } -#include "virtualcall.h" +class Y { +public: + virtual void foobar(); + Y() {foobar();} +}; -#define AS_SYSTEM -#include "virtualcall.h" -#undef AS_SYSTEM +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; +}