r311877 - [analyzer][GSoC] Re-implemente current virtual calls checker in a path-sensitive way
Gabor Horvath via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 28 01:44:43 PDT 2017
Author: xazax
Date: Mon Aug 28 01:44:43 2017
New Revision: 311877
URL: http://llvm.org/viewvc/llvm-project?rev=311877&view=rev
Log:
[analyzer][GSoC] Re-implemente current virtual calls checker in a path-sensitive way
Patch by: Xin Wang
Differential Revision: https://reviews.llvm.org/D34275
Modified:
cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
cfe/trunk/test/Analysis/virtualcall.cpp
cfe/trunk/test/Analysis/virtualcall.h
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp?rev=311877&r1=311876&r2=311877&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp Mon Aug 28 01:44:43 2017
@@ -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 {
-
-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));
+enum class ObjectState : bool { CtorCalled, DtorCalled };
+} // end namespace
+ // FIXME: Ascending over StackFrameContext maybe another method.
+
+namespace llvm {
+template <> struct FoldingSetTrait<ObjectState> {
+ static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
+ ID.AddInteger(static_cast<int>(X));
}
+};
+} // end namespace llvm
- 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);
- }
+namespace {
+class VirtualCallChecker
+ : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
+ mutable std::unique_ptr<BugType> BT;
- /// This method returns an item from the worklist without removing it.
- WorkListUnit Dequeue() {
- assert(!WList.empty());
- return WList.back();
- }
+public:
+ // The flag to determine if pure virtual functions should be issued only.
+ DefaultBool IsPureOnly;
- 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();
+ void checkBeginFunction(CheckerContext &C) const;
+ void checkEndFunction(CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+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);
}
- }
-
- // 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);
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+ };
};
-} // end anonymous namespace
+} // end namespace
-//===----------------------------------------------------------------------===//
-// AST walking.
-//===----------------------------------------------------------------------===//
-
-void WalkAST::VisitChildren(Stmt *S) {
- for (Stmt *Child : S->children())
- if (Child)
- Visit(Child);
-}
+// GDM (generic data map) to the memregion of this for the ctor and dtor.
+REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
-void WalkAST::VisitCallExpr(CallExpr *CE) {
- VisitChildren(CE);
- if (IsInterprocedural)
- Enqueue(CE);
+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);
}
-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.
+// 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;
+ 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;
+ if (const Expr *Base = CME->getBase()->IgnoreImpCasts()) {
+ // The most derived class is marked final.
+ 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>() &&
+ if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
!MD->getParent()->hasAttr<FinalAttr>())
- ReportVirtualCall(CE, MD->isPure());
+ return true;
+ return false;
+}
+
+// The BeginFunction callback when enter a constructor or a destructor.
+void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
+ registerCtorDtorCallInState(true, C);
+}
- if (IsInterprocedural)
- Enqueue(CE);
+// The EndFunction callback when leave a constructor or a destructor.
+void VirtualCallChecker::checkEndFunction(CheckerContext &C) const {
+ registerCtorDtorCallInState(false, C);
}
-void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
- if (ReportPureOnly && !isPure)
+void VirtualCallChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto MC = dyn_cast<CXXMemberCall>(&Call);
+ if (!MC)
return;
- SmallString<100> buf;
- llvm::raw_svector_ostream os(buf);
+ 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());
- // 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;
- }
+ if (IsPureOnly && !MD->isPure())
+ return;
+ if (!isVirtualCall(CE))
+ return;
- os << "\n";
+ 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);
}
- PathDiagnosticLocation CELoc =
- PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- SourceRange R = CE->getCallee()->getSourceRange();
-
- os << "Call to ";
- if (isPure)
- os << "pure ";
+ 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);
+ }
+}
- os << "virtual function during ";
+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;
- if (isa<CXXConstructorDecl>(RootMethod))
- os << "construction ";
- else
- os << "destruction ";
+ ProgramStateRef State = C.getState();
+ auto &SVB = C.getSValBuilder();
- if (isPure)
- os << "has undefined behavior";
- else
- os << "will not dispatch to derived class";
+ // 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);
- BR.EmitBasicReport(AC->getDecl(), Checker,
- "Call to virtual function during construction or "
- "destruction",
- "C++ Object Lifecycle", os.str(), CELoc, R);
-}
+ C.addTransition(State);
+ return;
+ }
-//===----------------------------------------------------------------------===//
-// VirtualCallChecker
-//===----------------------------------------------------------------------===//
+ // 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);
-namespace {
-class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
-public:
- DefaultBool isInterprocedural;
- DefaultBool isPureOnly;
+ C.addTransition(State);
+ return;
+ }
+}
- 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();
- }
- }
+void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
+ const MemRegion *Reg,
+ CheckerContext &C) const {
+ ExplodedNode *N;
+ if (IsSink)
+ N = C.generateErrorNode();
+ else
+ N = C.generateNonFatalErrorNode();
- // 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();
- }
- }
-};
+ 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);
}
Modified: cfe/trunk/test/Analysis/virtualcall.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/virtualcall.cpp?rev=311877&r1=311876&r2=311877&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/virtualcall.cpp (original)
+++ cfe/trunk/test/Analysis/virtualcall.cpp Mon Aug 28 01:44:43 2017
@@ -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
-
-/* 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-output=text -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 -analyzer-output=text -verify -std=c++11 %s
+
+#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 at -2 {{{{^}}Call Path : foo <-- fCall to pure virtual function during construction has undefined behavior}}
-#endif
+ // expected-warning-re at -1 {{{{^}}Call to pure virtual function during construction}}
+ // expected-note-re at -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 at -3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}}
-#else
- // expected-warning-re at -5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}}
+ // expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+ // expected-note-re at -3 {{{{^}}This constructor of an object of type 'B' has not returned when the virtual method was called}}
+ // expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
#endif
-#endif
-
}
~B();
-
+
virtual int foo();
- virtual void bar() { foo(); }
-#if INTERPROCEDURAL
- // expected-warning-re at -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 at -2 {{{{^}}Call to virtual function during destruction}}
+ // expected-note-re at -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 at -2 {{{{^}}Call Path : fooCall to pure virtual function during construction has undefined behavior}}
-#else
- // expected-warning-re at -4 {{{{^}}Call to pure virtual function during construction has undefined behavior}}
-#endif
+A::A() {
+ f();
+// expected-note-re at -1 {{{{^}}This constructor of an object of type 'A' has not returned when the virtual method was called}}
+// expected-note-re at -2 {{{{^}}Calling 'A::f'}}
}
B::~B() {
this->B::foo(); // no-warning
this->B::bar();
- this->foo();
#if !PUREONLY
-#if INTERPROCEDURAL
- // expected-warning-re at -3 {{{{^}}Call Path : fooCall to virtual function during destruction will not dispatch to derived class}}
-#else
- // expected-warning-re at -5 {{{{^}}Call to virtual function during destruction will not dispatch to derived class}}
+ // expected-note-re at -2 {{{{^}}This destructor of an object of type '~B' has not returned when the virtual method was called}}
+ // expected-note-re at -3 {{{{^}}Calling 'B::bar'}}
#endif
+ this->foo();
+#if !PUREONLY
+ // expected-warning-re at -2 {{{{^}}Call to virtual function during destruction}}
+ // expected-note-re at -3 {{{{^}}This destructor of an object of type '~B' has not returned when the virtual method was called}}
+ // expected-note-re at -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 at -3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}}
-#else
- // expected-warning-re at -5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}}
-#endif
+ // expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+ // expected-note-re at -3 {{{{^}}This constructor of an object of type 'C' has not returned when the virtual method was called}}
+ // expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
#endif
}
@@ -112,30 +97,177 @@ public:
foo(); // no-warning
}
~E() { bar(); }
+#if !PUREONLY
+ // expected-note-re at -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 at -2 {{{{^}}Call to virtual function during construction}}
+ // expected-note-re at -3 {{{{^}}This constructor of an object of type 'H' has not returned when the virtual method was called}}
+ // expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
+#endif
+ H &h = *this;
+ h.f();
+#if !PUREONLY
+ // expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+ // expected-note-re at -3 {{{{^}}This constructor of an object of type 'H' has not returned when the virtual method was called}}
+ // expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
+#endif
+ }
+};
+
+class X {
+public:
+ X() {
+ g();
+#if !PUREONLY
+ // expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+ // expected-note-re at -3 {{{{^}}This constructor of an object of type 'X' has not returned when the virtual method was called}}
+ // expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
+#endif
+ }
+ X(int i) {
+ if (i > 0) {
+#if !PUREONLY
+ // expected-note-re at -2 {{{{^}}Taking true branch}}
+ // expected-note-re at -3 {{{{^}}Taking false branch}}
+#endif
+ X x(i - 1);
+#if !PUREONLY
+ // expected-note-re at -2 {{{{^}}Calling constructor for 'X'}}
+#endif
+ x.g(); // no warning
+ }
+ g();
+#if !PUREONLY
+ // expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+ // expected-note-re at -3 {{{{^}}This constructor of an object of type 'X' has not returned when the virtual method was called}}
+ // expected-note-re at -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 at -2 {{{{^}}This constructor of an object of type 'M' has not returned when the virtual method was called}}
+ // expected-note-re at -3 {{{{^}}Calling 'N::callFooOfM'}}
+#endif
+ }
+ virtual void foo();
+};
+void N::callFooOfM(M *m) {
+ m->foo();
+#if !PUREONLY
+ // expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+ // expected-note-re at -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 at -2 {{{{^}}Call to virtual function during construction}}
+ // expected-note-re at -3 {{{{^}}Call to virtual function during construction}}
+#endif
+ }
+ Y() { fooY(); }
+#if !PUREONLY
+ // expected-note-re at -2 {{{{^}}This constructor of an object of type 'Y' has not returned when the virtual method was called}}
+ // expected-note-re at -3 {{{{^}}Calling 'Y::fooY'}}
+#endif
+};
-#define AS_SYSTEM
-#include "virtualcall.h"
-#undef AS_SYSTEM
+int main() {
+ B b;
+#if PUREONLY
+ //expected-note-re at -2 {{{{^}}Calling default constructor for 'B'}}
+#else
+ //expected-note-re at -4 2{{{{^}}Calling default constructor for 'B'}}
+#endif
+ C c;
+#if !PUREONLY
+ //expected-note-re at -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 at -2 {{{{^}}Calling constructor for 'H'}}
+ //expected-note-re at -3 {{{{^}}Calling constructor for 'H'}}
+#endif
+ X x;
+#if !PUREONLY
+ //expected-note-re at -2 {{{{^}}Calling default constructor for 'X'}}
+#endif
+ X x1(1);
+#if !PUREONLY
+ //expected-note-re at -2 {{{{^}}Calling constructor for 'X'}}
+#endif
+ M m;
+#if !PUREONLY
+ //expected-note-re at -2 {{{{^}}Calling default constructor for 'M'}}
+#endif
+ Y *y = new Y;
+ delete y;
+ header::Z z;
+#if !PUREONLY
+ // expected-note-re at -2 {{{{^}}Calling default constructor for 'Z'}}
+#endif
+}
+#if !PUREONLY
+ //expected-note-re at -2 2{{{{^}}Calling '~E'}}
+#endif
Modified: cfe/trunk/test/Analysis/virtualcall.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/virtualcall.h?rev=311877&r1=311876&r2=311877&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/virtualcall.h (original)
+++ cfe/trunk/test/Analysis/virtualcall.h Mon Aug 28 01:44:43 2017
@@ -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 at -3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}}
-#else
- // expected-warning-re at -5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}}
-#endif
+ // expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+ // expected-note-re at -3 {{{{^}}This constructor of an object of type 'Z' has not returned when the virtual method was called}}
+ // expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
#endif
-
}
-
virtual int foo();
};
}
-
-#endif
More information about the cfe-commits
mailing list