diff options
| -rw-r--r-- | clang/include/clang/Analysis/AnalysisDeclContext.h | 1 | ||||
| -rw-r--r-- | clang/include/clang/Analysis/CFG.h | 78 | ||||
| -rw-r--r-- | clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h | 10 | ||||
| -rw-r--r-- | clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 2 | ||||
| -rw-r--r-- | clang/lib/Analysis/AnalysisDeclContext.cpp | 4 | ||||
| -rw-r--r-- | clang/lib/Analysis/CFG.cpp | 86 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp | 1 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp | 8 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 23 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp | 1 | ||||
| -rw-r--r-- | clang/test/Analysis/analyzer-config.c | 3 | ||||
| -rw-r--r-- | clang/test/Analysis/analyzer-config.cpp | 3 | ||||
| -rw-r--r-- | clang/test/Analysis/cfg-rich-constructors.cpp | 46 | ||||
| -rw-r--r-- | clang/test/Analysis/cfg.cpp | 27 |
14 files changed, 255 insertions, 38 deletions
diff --git a/clang/include/clang/Analysis/AnalysisDeclContext.h b/clang/include/clang/Analysis/AnalysisDeclContext.h index c6ea57ded18..63d467616b4 100644 --- a/clang/include/clang/Analysis/AnalysisDeclContext.h +++ b/clang/include/clang/Analysis/AnalysisDeclContext.h @@ -439,6 +439,7 @@ public: bool synthesizeBodies = false, bool addStaticInitBranches = false, bool addCXXNewAllocator = true, + bool addRichCXXConstructors = true, CodeInjector *injector = nullptr); AnalysisDeclContext *getContext(const Decl *D); diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h index cfedb2aa8ed..8236e043840 100644 --- a/clang/include/clang/Analysis/CFG.h +++ b/clang/include/clang/Analysis/CFG.h @@ -15,7 +15,7 @@ #ifndef LLVM_CLANG_ANALYSIS_CFG_H #define LLVM_CLANG_ANALYSIS_CFG_H -#include "clang/AST/Stmt.h" +#include "clang/AST/ExprCXX.h" #include "clang/Analysis/Support/BumpVector.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/DenseMap.h" @@ -55,11 +55,15 @@ class CFGElement { public: enum Kind { // main kind - Statement, Initializer, NewAllocator, LifetimeEnds, LoopExit, + // stmt kind + Statement, + Constructor, + STMT_BEGIN = Statement, + STMT_END = Constructor, // dtor kind AutomaticObjectDtor, DeleteDtor, @@ -117,7 +121,9 @@ public: class CFGStmt : public CFGElement { public: - CFGStmt(Stmt *S) : CFGElement(Statement, S) {} + explicit CFGStmt(Stmt *S, Kind K = Statement) : CFGElement(K, S) { + assert(isKind(*this)); + } const Stmt *getStmt() const { return static_cast<const Stmt *>(Data1.getPointer()); @@ -126,10 +132,66 @@ public: private: friend class CFGElement; + static bool isKind(const CFGElement &E) { + return E.getKind() >= STMT_BEGIN && E.getKind() <= STMT_END; + } + +protected: CFGStmt() = default; +}; + +// This is bulky data for CFGConstructor which would not fit into the +// CFGElement's room (pair of pointers). Contains the information +// necessary to express what memory is being initialized by +// the construction. +class ConstructionContext { + // The construction site - the statement that triggered the construction + // for one of its parts. For instance, stack variable declaration statement + // triggers construction of itself or its elements if it's an array, + // new-expression triggers construction of the newly allocated object(s). + Stmt *Trigger = nullptr; + +public: + ConstructionContext() = default; + ConstructionContext(Stmt *Trigger) : Trigger(Trigger) {} + + bool isNull() const { return Trigger == nullptr; } + + const Stmt *getTriggerStmt() const { return Trigger; } + + const ConstructionContext *getPersistentCopy(BumpVectorContext &C) const { + ConstructionContext *CC = C.getAllocator().Allocate<ConstructionContext>(); + *CC = *this; + return CC; + } +}; + +/// CFGConstructor - Represents C++ constructor call. Maintains information +/// necessary to figure out what memory is being initialized by the +/// constructor expression. For now this is only used by the analyzer's CFG. +class CFGConstructor : public CFGStmt { +public: + explicit CFGConstructor(CXXConstructExpr *CE, const ConstructionContext *C) + : CFGStmt(CE, Constructor) { + assert(!C->isNull()); + Data2.setPointer(const_cast<ConstructionContext *>(C)); + } + + const ConstructionContext *getConstructionContext() const { + return static_cast<ConstructionContext *>(Data2.getPointer()); + } + + const Stmt *getTriggerStmt() const { + return getConstructionContext()->getTriggerStmt(); + } + +private: + friend class CFGElement; + + CFGConstructor() = default; static bool isKind(const CFGElement &E) { - return E.getKind() == Statement; + return E.getKind() == Constructor; } }; @@ -137,7 +199,7 @@ private: /// constructor's initialization list. class CFGInitializer : public CFGElement { public: - CFGInitializer(CXXCtorInitializer *initializer) + explicit CFGInitializer(CXXCtorInitializer *initializer) : CFGElement(Initializer, initializer) {} CXXCtorInitializer* getInitializer() const { @@ -747,6 +809,11 @@ public: Elements.push_back(CFGStmt(statement), C); } + void appendConstructor(CXXConstructExpr *CE, const ConstructionContext &CC, + BumpVectorContext &C) { + Elements.push_back(CFGConstructor(CE, CC.getPersistentCopy(C)), C); + } + void appendInitializer(CXXCtorInitializer *initializer, BumpVectorContext &C) { Elements.push_back(CFGInitializer(initializer), C); @@ -855,6 +922,7 @@ public: bool AddStaticInitBranches = false; bool AddCXXNewAllocator = false; bool AddCXXDefaultInitExprInCtors = false; + bool AddRichCXXConstructors = false; BuildOptions() = default; diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index 5ddf7d79c8d..f6bd78884fd 100644 --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -231,6 +231,9 @@ private: /// \sa IncludeLoopExitInCFG Optional<bool> IncludeLoopExitInCFG; + /// \sa IncludeRichConstructorsInCFG + Optional<bool> IncludeRichConstructorsInCFG; + /// \sa mayInlineCXXStandardLibrary Optional<bool> InlineCXXStandardLibrary; @@ -444,6 +447,13 @@ public: /// the values "true" and "false". bool includeLoopExitInCFG(); + /// Returns whether or not construction site information should be included + /// in the CFG C++ constructor elements. + /// + /// This is controlled by the 'cfg-rich-constructors' config options, + /// which accepts the values "true" and "false". + bool includeRichConstructorsInCFG(); + /// Returns whether or not C++ standard library functions may be considered /// for inlining. /// diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index ef06d12795f..baac989706a 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -194,7 +194,7 @@ public: void processCFGElement(const CFGElement E, ExplodedNode *Pred, unsigned StmtIdx, NodeBuilderContext *Ctx) override; - void ProcessStmt(const CFGStmt S, ExplodedNode *Pred); + void ProcessStmt(const Stmt *S, ExplodedNode *Pred); void ProcessLoopExit(const Stmt* S, ExplodedNode *Pred); diff --git a/clang/lib/Analysis/AnalysisDeclContext.cpp b/clang/lib/Analysis/AnalysisDeclContext.cpp index 9874bb87b28..2b8259bad72 100644 --- a/clang/lib/Analysis/AnalysisDeclContext.cpp +++ b/clang/lib/Analysis/AnalysisDeclContext.cpp @@ -67,7 +67,8 @@ AnalysisDeclContextManager::AnalysisDeclContextManager( ASTContext &ASTCtx, bool useUnoptimizedCFG, bool addImplicitDtors, bool addInitializers, bool addTemporaryDtors, bool addLifetime, bool addLoopExit, bool synthesizeBodies, bool addStaticInitBranch, - bool addCXXNewAllocator, CodeInjector *injector) + bool addCXXNewAllocator, bool addRichCXXConstructors, + CodeInjector *injector) : Injector(injector), FunctionBodyFarm(ASTCtx, injector), SynthesizeBodies(synthesizeBodies) { cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG; @@ -78,6 +79,7 @@ AnalysisDeclContextManager::AnalysisDeclContextManager( cfgBuildOptions.AddLoopExit = addLoopExit; cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator; + cfgBuildOptions.AddRichCXXConstructors = addRichCXXConstructors; } void AnalysisDeclContextManager::clear() { Contexts.clear(); } diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 714b85d3a9f..63f1d724eb7 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -472,6 +472,11 @@ class CFGBuilder { using LabelSetTy = llvm::SmallSetVector<LabelDecl *, 8>; LabelSetTy AddressTakenLabels; + // Information about the currently visited C++ object construction site. + // This is set in the construction trigger and read when the constructor + // itself is being visited. + ConstructionContext CurrentConstructionContext = {}; + bool badCFG = false; const CFG::BuildOptions &BuildOpts; @@ -643,6 +648,18 @@ private: return Block; } + // Scan the child statement \p Child to find the constructor that might + // have been directly triggered by the current node, \p Trigger. If such + // constructor has been found, set current construction context to point + // to the trigger statement. The construction context will be unset once + // it is consumed when the CFG building procedure processes the + // construct-expression and adds the respective CFGConstructor element. + void EnterConstructionContextIfNecessary(Stmt *Trigger, Stmt *Child); + // Unset the construction context after consuming it. This is done immediately + // after adding the CFGConstructor element, so there's no need to + // do this manually in every Visit... function. + void ExitConstructionContext(); + void autoCreateBlock() { if (!Block) Block = createBlock(); } CFGBlock *createBlock(bool add_successor = true); CFGBlock *createNoReturnBlock(); @@ -682,6 +699,20 @@ private: B->appendStmt(const_cast<Stmt*>(S), cfg->getBumpVectorContext()); } + void appendConstructor(CFGBlock *B, CXXConstructExpr *CE) { + if (BuildOpts.AddRichCXXConstructors) { + if (!CurrentConstructionContext.isNull()) { + B->appendConstructor(CE, CurrentConstructionContext, + cfg->getBumpVectorContext()); + ExitConstructionContext(); + return; + } + } + + // No valid construction context found. Fall back to statement. + B->appendStmt(CE, cfg->getBumpVectorContext()); + } + void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) { B->appendInitializer(I, cfg->getBumpVectorContext()); } @@ -1116,6 +1147,26 @@ static const VariableArrayType *FindVA(const Type *t) { return nullptr; } +void CFGBuilder::EnterConstructionContextIfNecessary(Stmt *Trigger, + Stmt *Child) { + if (!BuildOpts.AddRichCXXConstructors) + return; + if (!Child) + return; + if (auto *Constructor = dyn_cast<CXXConstructExpr>(Child)) { + assert(CurrentConstructionContext.isNull() && + "Already within a construction context!"); + CurrentConstructionContext = ConstructionContext(Trigger); + } +} + +void CFGBuilder::ExitConstructionContext() { + assert(!CurrentConstructionContext.isNull() && + "Cannot exit construction context without the context!"); + CurrentConstructionContext = ConstructionContext(); +} + + /// BuildCFG - Constructs a CFG from an AST (a Stmt*). The AST can represent an /// arbitrary statement. Examples include a single expression or a function /// body (compound statement). The ownership of the returned CFG is @@ -3872,7 +3923,7 @@ CFGBlock *CFGBuilder::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc) { autoCreateBlock(); - appendStmt(Block, C); + appendConstructor(Block, C); return VisitChildren(C); } @@ -3882,15 +3933,22 @@ CFGBlock *CFGBuilder::VisitCXXNewExpr(CXXNewExpr *NE, autoCreateBlock(); appendStmt(Block, NE); + EnterConstructionContextIfNecessary( + NE, const_cast<CXXConstructExpr *>(NE->getConstructExpr())); + if (NE->getInitializer()) Block = Visit(NE->getInitializer()); + if (BuildOpts.AddCXXNewAllocator) appendNewAllocator(Block, NE); + if (NE->isArray()) Block = Visit(NE->getArraySize()); + for (CXXNewExpr::arg_iterator I = NE->placement_arg_begin(), E = NE->placement_arg_end(); I != E; ++I) Block = Visit(*I); + return Block; } @@ -4210,11 +4268,12 @@ std::unique_ptr<CFG> CFG::buildCFG(const Decl *D, Stmt *Statement, const CXXDestructorDecl * CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { switch (getKind()) { - case CFGElement::Statement: case CFGElement::Initializer: case CFGElement::NewAllocator: case CFGElement::LoopExit: case CFGElement::LifetimeEnds: + case CFGElement::Statement: + case CFGElement::Constructor: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -4343,8 +4402,8 @@ public: switch (stmt->getStmtClass()) { case Stmt::DeclStmtClass: - DeclMap[cast<DeclStmt>(stmt)->getSingleDecl()] = P; - break; + DeclMap[cast<DeclStmt>(stmt)->getSingleDecl()] = P; + break; case Stmt::IfStmtClass: { const VarDecl *var = cast<IfStmt>(stmt)->getConditionVariable(); if (var) @@ -4575,14 +4634,19 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, if (isa<CXXOperatorCallExpr>(S)) { OS << " (OperatorCall)"; - } - else if (isa<CXXBindTemporaryExpr>(S)) { + } else if (isa<CXXBindTemporaryExpr>(S)) { OS << " (BindTemporary)"; - } - else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) { - OS << " (CXXConstructExpr, " << CCE->getType().getAsString() << ")"; - } - else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) { + } else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) { + OS << " (CXXConstructExpr, "; + if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) { + if (const Stmt *S = CE->getTriggerStmt()) + Helper.handledStmt((const_cast<Stmt *>(S)), OS); + else + llvm_unreachable("Unexpected trigger kind!"); + OS << ", "; + } + OS << CCE->getType().getAsString() << ")"; + } else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) { OS << " (" << CE->getStmtClassName() << ", " << CE->getCastKindName() << ", " << CE->getType().getAsString() diff --git a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 1cc08f0d9fe..53199f746c3 100644 --- a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -29,6 +29,7 @@ AnalysisManager::AnalysisManager( Options.shouldSynthesizeBodies(), Options.shouldConditionalizeStaticInitializers(), /*addCXXNewAllocator=*/true, + Options.includeRichConstructorsInCFG(), injector), Ctx(ASTCtx), Diags(diags), LangOpts(lang), PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index ca05e38608c..0e3e207c15e 100644 --- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -204,7 +204,13 @@ bool AnalyzerOptions::includeLifetimeInCFG() { bool AnalyzerOptions::includeLoopExitInCFG() { return getBooleanOption(IncludeLoopExitInCFG, "cfg-loopexit", - /* Default = */ false); + /* Default = */ false); +} + +bool AnalyzerOptions::includeRichConstructorsInCFG() { + return getBooleanOption(IncludeRichConstructorsInCFG, + "cfg-rich-constructors", + /* Default = */ true); } bool AnalyzerOptions::mayInlineCXXStandardLibrary() { diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 3df69e891b7..fef7116d905 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -454,10 +454,11 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, switch (E.getKind()) { case CFGElement::Statement: - ProcessStmt(const_cast<Stmt*>(E.castAs<CFGStmt>().getStmt()), Pred); + case CFGElement::Constructor: + ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred); return; case CFGElement::Initializer: - ProcessInitializer(E.castAs<CFGInitializer>().getInitializer(), Pred); + ProcessInitializer(E.castAs<CFGInitializer>(), Pred); return; case CFGElement::NewAllocator: ProcessNewAllocator(E.castAs<CFGNewAllocator>().getAllocatorExpr(), @@ -479,7 +480,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, } static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, - const CFGStmt S, + const Stmt *S, const ExplodedNode *Pred, const LocationContext *LC) { @@ -492,17 +493,17 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return true; // Is this on a non-expression? - if (!isa<Expr>(S.getStmt())) + if (!isa<Expr>(S)) return true; // Run before processing a call. - if (CallEvent::isCallStmt(S.getStmt())) + if (CallEvent::isCallStmt(S)) return true; // Is this an expression that is consumed by another expression? If so, // postpone cleaning out the state. ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap(); - return !PM.isConsumedExpr(cast<Expr>(S.getStmt())); + return !PM.isConsumedExpr(cast<Expr>(S)); } void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, @@ -594,20 +595,20 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, } } -void ExprEngine::ProcessStmt(const CFGStmt S, - ExplodedNode *Pred) { +void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); - const Stmt *currStmt = S.getStmt(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), currStmt->getLocStart(), "Error evaluating statement"); // Remove dead bindings and symbols. ExplodedNodeSet CleanedStates; - if (shouldRemoveDeadBindings(AMgr, S, Pred, Pred->getLocationContext())){ - removeDead(Pred, CleanedStates, currStmt, Pred->getLocationContext()); + if (shouldRemoveDeadBindings(AMgr, currStmt, Pred, + Pred->getLocationContext())) { + removeDead(Pred, CleanedStates, currStmt, + Pred->getLocationContext()); } else CleanedStates.Add(Pred); diff --git a/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 3d4b377627f..561490b09aa 100644 --- a/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -551,6 +551,7 @@ getLocationForCaller(const StackFrameContext *SFC, switch (Source.getKind()) { case CFGElement::Statement: + case CFGElement::Constructor: return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(), SM, CallerCtx); case CFGElement::Initializer: { diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c index 770ff3b404f..89d6ccca5ad 100644 --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -15,6 +15,7 @@ void foo() { // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false // CHECK-NEXT: cfg-loopexit = false +// CHECK-NEXT: cfg-rich-constructors = true // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: exploration_strategy = dfs // CHECK-NEXT: faux-bodies = true @@ -32,4 +33,4 @@ void foo() { // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 20 +// CHECK-NEXT: num-entries = 21 diff --git a/clang/test/Analysis/analyzer-config.cpp b/clang/test/Analysis/analyzer-config.cpp index a6e1df87458..2629aeeb968 100644 --- a/clang/test/Analysis/analyzer-config.cpp +++ b/clang/test/Analysis/analyzer-config.cpp @@ -26,6 +26,7 @@ public: // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false // CHECK-NEXT: cfg-loopexit = false +// CHECK-NEXT: cfg-rich-constructors = true // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: exploration_strategy = dfs // CHECK-NEXT: faux-bodies = true @@ -43,4 +44,4 @@ public: // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 25 +// CHECK-NEXT: num-entries = 26 diff --git a/clang/test/Analysis/cfg-rich-constructors.cpp b/clang/test/Analysis/cfg-rich-constructors.cpp new file mode 100644 index 00000000000..0b5a2c8ff5e --- /dev/null +++ b/clang/test/Analysis/cfg-rich-constructors.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++11 %s > %t 2>&1 +// RUN: FileCheck --input-file=%t %s + +class C { +public: + C(); + C(C *); +}; + +typedef __typeof(sizeof(int)) size_t; +void *operator new(size_t size, void *placement); + +namespace operator_new { + +// CHECK: void operatorNewWithConstructor() +// CHECK: 1: CFGNewAllocator(C *) +// CHECK-NEXT: 2: (CXXConstructExpr, [B1.3], class C) +// CHECK-NEXT: 3: new C([B1.2]) +void operatorNewWithConstructor() { + new C(); +} + +// CHECK: void operatorNewWithConstructorWithOperatorNewWithContstructor() +// CHECK: 1: CFGNewAllocator(C *) +// CHECK-NEXT: 2: CFGNewAllocator(C *) +// CHECK-NEXT: 3: (CXXConstructExpr, [B1.4], class C) +// CHECK-NEXT: 4: new C([B1.3]) +// CHECK-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.6], class C) +// CHECK-NEXT: 6: new C([B1.5]) +void operatorNewWithConstructorWithOperatorNewWithContstructor() { + new C(new C()); +} + +// CHECK: void operatorPlacementNewWithConstructorWithinPlacementArgument() +// CHECK: 1: CFGNewAllocator(C *) +// CHECK-NEXT: 2: (CXXConstructExpr, [B1.3], class C) +// CHECK-NEXT: 3: new C([B1.2]) +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, BitCast, void *) +// CHECK-NEXT: 5: CFGNewAllocator(C *) +// CHECK-NEXT: 6: (CXXConstructExpr, [B1.7], class C) +// CHECK-NEXT: 7: new ([B1.4]) C([B1.6]) +void operatorPlacementNewWithConstructorWithinPlacementArgument() { + new (new C()) C(); +} + +} // namespace operator_new diff --git a/clang/test/Analysis/cfg.cpp b/clang/test/Analysis/cfg.cpp index 208277343cd..38e22481749 100644 --- a/clang/test/Analysis/cfg.cpp +++ b/clang/test/Analysis/cfg.cpp @@ -1,5 +1,16 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++11 %s > %t 2>&1 -// RUN: FileCheck --input-file=%t %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++11 -analyzer-config cfg-rich-constructors=false %s > %t 2>&1 +// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,WARNINGS %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++11 -analyzer-config cfg-rich-constructors=true %s > %t 2>&1 +// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,ANALYZER %s + +// This file tests how we construct two different flavors of the Clang CFG - +// the CFG used by the Sema analysis-based warnings and the CFG used by the +// static analyzer. The difference in the behavior is checked via FileCheck +// prefixes (WARNINGS and ANALYZER respectively). When introducing new analyzer +// flags, no new run lines should be added - just these flags would go to the +// respective line depending on where is it turned on and where is it turned +// off. Feel free to add tests that test only one of the CFG flavors if you're +// not sure how the other flavor is supposed to work in your case. // CHECK-LABEL: void checkWrap(int i) // CHECK: ENTRY @@ -116,7 +127,8 @@ public: // CHECK-NEXT: Succs (1): B1 // CHECK: [B1] // CHECK-NEXT: 1: CFGNewAllocator(A *) -// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// WARNINGS-NEXT: 2: (CXXConstructExpr, class A) +// ANALYZER-NEXT: 2: (CXXConstructExpr, [B1.3], class A) // CHECK-NEXT: 3: new A([B1.2]) // CHECK-NEXT: 4: A *a = new A(); // CHECK-NEXT: 5: a @@ -138,7 +150,8 @@ void test_deletedtor() { // CHECK: [B1] // CHECK-NEXT: 1: 5 // CHECK-NEXT: 2: CFGNewAllocator(A *) -// CHECK-NEXT: 3: (CXXConstructExpr, class A [5]) +// WARNINGS-NEXT: 3: (CXXConstructExpr, class A [5]) +// ANALYZER-NEXT: 3: (CXXConstructExpr, [B1.4], class A [5]) // CHECK-NEXT: 4: new A {{\[\[}}B1.1]] // CHECK-NEXT: 5: A *a = new A [5]; // CHECK-NEXT: 6: a @@ -331,7 +344,8 @@ int test_enum_with_extension_default(enum MyEnum value) { // CHECK-NEXT: 3: [B1.2] (ImplicitCastExpr, ArrayToPointerDecay, int *) // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, BitCast, void *) // CHECK-NEXT: 5: CFGNewAllocator(MyClass *) -// CHECK-NEXT: 6: (CXXConstructExpr, class MyClass) +// WARNINGS-NEXT: 6: (CXXConstructExpr, class MyClass) +// ANALYZER-NEXT: 6: (CXXConstructExpr, [B1.7], class MyClass) // CHECK-NEXT: 7: new ([B1.4]) MyClass([B1.6]) // CHECK-NEXT: 8: MyClass *obj = new (buffer) MyClass(); // CHECK-NEXT: Preds (1): B2 @@ -363,7 +377,8 @@ void test_placement_new() { // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, BitCast, void *) // CHECK-NEXT: 5: 5 // CHECK-NEXT: 6: CFGNewAllocator(MyClass *) -// CHECK-NEXT: 7: (CXXConstructExpr, class MyClass [5]) +// WARNINGS-NEXT: 7: (CXXConstructExpr, class MyClass [5]) +// ANALYZER-NEXT: 7: (CXXConstructExpr, [B1.8], class MyClass [5]) // CHECK-NEXT: 8: new ([B1.4]) MyClass {{\[\[}}B1.5]] // CHECK-NEXT: 9: MyClass *obj = new (buffer) MyClass [5]; // CHECK-NEXT: Preds (1): B2 |

