[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
This revision was automatically updated to reflect the committed changes. Closed by commit rC324668: [CFG] Add extra context to C++ constructor statement elements. (authored by dergachev, committed by ). Changed prior to commit: https://reviews.llvm.org/D42672?vs=133139=133500#toc Repository: rL LLVM https://reviews.llvm.org/D42672 Files: include/clang/Analysis/AnalysisDeclContext.h include/clang/Analysis/CFG.h include/clang/StaticAnalyzer/Core/AnalyzerOptions.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/Analysis/AnalysisDeclContext.cpp lib/Analysis/CFG.cpp lib/StaticAnalyzer/Core/AnalysisManager.cpp lib/StaticAnalyzer/Core/AnalyzerOptions.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Core/PathDiagnostic.cpp test/Analysis/analyzer-config.c test/Analysis/analyzer-config.cpp test/Analysis/cfg-rich-constructors.cpp test/Analysis/cfg.cpp Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h === --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -231,6 +231,9 @@ /// \sa IncludeLoopExitInCFG Optional IncludeLoopExitInCFG; + /// \sa IncludeRichConstructorsInCFG + Optional IncludeRichConstructorsInCFG; + /// \sa mayInlineCXXStandardLibrary Optional InlineCXXStandardLibrary; @@ -444,6 +447,13 @@ /// 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. /// Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h === --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -194,7 +194,7 @@ 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); Index: include/clang/Analysis/AnalysisDeclContext.h === --- include/clang/Analysis/AnalysisDeclContext.h +++ include/clang/Analysis/AnalysisDeclContext.h @@ -439,6 +439,7 @@ bool synthesizeBodies = false, bool addStaticInitBranches = false, bool addCXXNewAllocator = true, + bool addRichCXXConstructors = true, CodeInjector *injector = nullptr); AnalysisDeclContext *getContext(const Decl *D); Index: include/clang/Analysis/CFG.h === --- include/clang/Analysis/CFG.h +++ 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 @@ 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,27 +121,85 @@ 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(Data1.getPointer()); } private: friend class CFGElement; + static bool isKind(const CFGElement ) { +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: +
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
This revision was automatically updated to reflect the committed changes. Closed by commit rL324668: [CFG] Add extra context to C++ constructor statement elements. (authored by dergachev, committed by ). Herald added a subscriber: llvm-commits. Changed prior to commit: https://reviews.llvm.org/D42672?vs=133139=133499#toc Repository: rL LLVM https://reviews.llvm.org/D42672 Files: cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h cfe/trunk/include/clang/Analysis/CFG.h cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp cfe/trunk/lib/Analysis/CFG.cpp cfe/trunk/lib/StaticAnalyzer/Core/AnalysisManager.cpp cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp cfe/trunk/test/Analysis/analyzer-config.c cfe/trunk/test/Analysis/analyzer-config.cpp cfe/trunk/test/Analysis/cfg-rich-constructors.cpp cfe/trunk/test/Analysis/cfg.cpp Index: cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h === --- cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -231,6 +231,9 @@ /// \sa IncludeLoopExitInCFG Optional IncludeLoopExitInCFG; + /// \sa IncludeRichConstructorsInCFG + Optional IncludeRichConstructorsInCFG; + /// \sa mayInlineCXXStandardLibrary Optional InlineCXXStandardLibrary; @@ -444,6 +447,13 @@ /// 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. /// Index: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h === --- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -194,7 +194,7 @@ 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); Index: cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h === --- cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h +++ cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h @@ -439,6 +439,7 @@ bool synthesizeBodies = false, bool addStaticInitBranches = false, bool addCXXNewAllocator = true, + bool addRichCXXConstructors = true, CodeInjector *injector = nullptr); AnalysisDeclContext *getContext(const Decl *D); Index: cfe/trunk/include/clang/Analysis/CFG.h === --- cfe/trunk/include/clang/Analysis/CFG.h +++ cfe/trunk/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 @@ 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,27 +121,85 @@ 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(Data1.getPointer()); } private: friend class CFGElement; + static bool isKind(const CFGElement ) { +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
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ added a comment. All right, so this change is indeed safe, i.e. no crashes or changes in analyzer behavior so far on my large codebase run. Will commit now. https://reviews.llvm.org/D42672 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ updated this revision to Diff 133139. NoQ added a comment. Make CFGConstructor a sub-class of CFGStmt. I failed to notice any compile time regressions for now; if any, they must be super bottlenecked on CFG construction or analysis-based warnings. After poking @rsmith in the chat a little bit, we decided to go the easy way and make constructor a statement. https://reviews.llvm.org/D42672 Files: include/clang/Analysis/AnalysisDeclContext.h include/clang/Analysis/CFG.h include/clang/StaticAnalyzer/Core/AnalyzerOptions.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/Analysis/AnalysisDeclContext.cpp lib/Analysis/CFG.cpp lib/StaticAnalyzer/Core/AnalysisManager.cpp lib/StaticAnalyzer/Core/AnalyzerOptions.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Core/PathDiagnostic.cpp test/Analysis/analyzer-config.c test/Analysis/analyzer-config.cpp test/Analysis/cfg-rich-constructors.cpp test/Analysis/cfg.cpp Index: test/Analysis/cfg.cpp === --- test/Analysis/cfg.cpp +++ 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 @@ // 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 @@ // 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 @@ // 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 @@ // 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 Index: test/Analysis/cfg-rich-constructors.cpp === --- /dev/null +++ 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:
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ added inline comments. Comment at: include/clang/Analysis/CFG.h:153 + + ConstructionContext() = default; + ConstructionContext(CXXConstructExpr *Constructor, Stmt *Trigger) xazax.hun wrote: > Maybe I am getting this wrong, but I think in this case the members will be > default initialized and will get indeterminate values. > See: http://en.cppreference.com/w/cpp/language/default_initialization > > Default initialization is performed in three situations: > > .. > > 3) when a base class or a non-static data member is not mentioned in a > > constructor initializer list and that constructor is called. > > > > > > The effects of default initialization are: > > > > if T is a non-POD (until C++11) class type ... > > > > if T is an array type, every element of the array is default-initialized; > > > > otherwise, nothing is done: the objects with automatic storage duration > > (and their subobjects) are initialized to indeterminate values. Ew, right. I should be more careful (no idea how it works). Comment at: lib/Analysis/CFG.cpp:4402 + stmt = SE->getStmt(); +else if (auto CE = BI->getAs()) + stmt = CE->getConstructor(); xazax.hun wrote: > So this is one of the places where subclassing would help? Could you measure > the compile time regression after making `CFGStmt`'s `isKind` more complex? Yeah, any user of the new mode would be surprised that constructors are not statements, which is indeed annoying. But i wanted to play super safe. I'm not seeing any compile time regressions yet after making `isKind` more complex - i.e. ran "`clang sqlite3.c`" release-without-asserts 100 times before and 100 times after, mean compilation time difference around 0.04% (in the bad direction, i.e. after-time is 0.04% worse than before-time), p-value (Welch's t-test) around 0.7. I guess i could probably run llvm compilation before and after for more coverage, but that wouldn't be statistically reliable. Do we have any more reliable benchmarks? https://reviews.llvm.org/D42672 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ updated this revision to Diff 132908. NoQ marked an inline comment as done. NoQ added a comment. Fix uninitialized variables. https://reviews.llvm.org/D42672 Files: include/clang/Analysis/AnalysisDeclContext.h include/clang/Analysis/CFG.h include/clang/StaticAnalyzer/Core/AnalyzerOptions.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/Analysis/AnalysisDeclContext.cpp lib/Analysis/CFG.cpp lib/Analysis/LiveVariables.cpp lib/StaticAnalyzer/Core/AnalysisManager.cpp lib/StaticAnalyzer/Core/AnalyzerOptions.cpp lib/StaticAnalyzer/Core/CoreEngine.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/PathDiagnostic.cpp test/Analysis/analyzer-config.c test/Analysis/analyzer-config.cpp test/Analysis/cfg-rich-constructors.cpp test/Analysis/cfg.cpp Index: test/Analysis/cfg.cpp === --- test/Analysis/cfg.cpp +++ 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 @@ // 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 @@ // 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 @@ // 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 @@ // 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 Index: test/Analysis/cfg-rich-constructors.cpp === --- /dev/null +++ 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(); +} + +//
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
xazax.hun added inline comments. Comment at: include/clang/Analysis/CFG.h:153 + + ConstructionContext() = default; + ConstructionContext(CXXConstructExpr *Constructor, Stmt *Trigger) Maybe I am getting this wrong, but I think in this case the members will be default initialized and will get indeterminate values. See: http://en.cppreference.com/w/cpp/language/default_initialization > Default initialization is performed in three situations: > .. > 3) when a base class or a non-static data member is not mentioned in a > constructor initializer list and that constructor is called. > > > The effects of default initialization are: > > if T is a non-POD (until C++11) class type ... > > if T is an array type, every element of the array is default-initialized; > > otherwise, nothing is done: the objects with automatic storage duration (and > their subobjects) are initialized to indeterminate values. Comment at: lib/Analysis/CFG.cpp:4402 + stmt = SE->getStmt(); +else if (auto CE = BI->getAs()) + stmt = CE->getConstructor(); So this is one of the places where subclassing would help? Could you measure the compile time regression after making `CFGStmt`'s `isKind` more complex? https://reviews.llvm.org/D42672 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ updated this revision to Diff 132685. NoQ added a comment. Rename the method, add comments. https://reviews.llvm.org/D42672 Files: include/clang/Analysis/AnalysisDeclContext.h include/clang/Analysis/CFG.h include/clang/StaticAnalyzer/Core/AnalyzerOptions.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/Analysis/AnalysisDeclContext.cpp lib/Analysis/CFG.cpp lib/Analysis/LiveVariables.cpp lib/StaticAnalyzer/Core/AnalysisManager.cpp lib/StaticAnalyzer/Core/AnalyzerOptions.cpp lib/StaticAnalyzer/Core/CoreEngine.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/PathDiagnostic.cpp test/Analysis/analyzer-config.c test/Analysis/analyzer-config.cpp test/Analysis/cfg-rich-constructors.cpp test/Analysis/cfg.cpp Index: test/Analysis/cfg.cpp === --- test/Analysis/cfg.cpp +++ 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 @@ // 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 @@ // 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 @@ // 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 @@ // 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 Index: test/Analysis/cfg-rich-constructors.cpp === --- /dev/null +++ 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
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ added inline comments. Comment at: lib/Analysis/CFG.cpp:653 + // triggered by the given trigger. + void VisitForConstructionContext(Stmt *Trigger, Stmt *Child); + dcoughlin wrote: > I find the name of this method very confusing. It sounds like you are > visiting an AST node called a 'ForConstructionContext', but that is not what > this function does. It is also not clear from either the name or the comment > what the method is supposed to do. Can you be more precise? > > You can fix this in a follow-up commit if you like. Yeah i guess i just followed the `VisitForTemporaryDtors` pattern^^ Will fix. https://reviews.llvm.org/D42672 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
dcoughlin accepted this revision. dcoughlin added a comment. This revision is now accepted and ready to land. This looks good to me, although as I noted inline I think both the name and the comment for VisitForConstructionContext() are confusing. If you can be more precise I think it would help future maintainers. Comment at: lib/Analysis/CFG.cpp:653 + // triggered by the given trigger. + void VisitForConstructionContext(Stmt *Trigger, Stmt *Child); + I find the name of this method very confusing. It sounds like you are visiting an AST node called a 'ForConstructionContext', but that is not what this function does. It is also not clear from either the name or the comment what the method is supposed to do. Can you be more precise? You can fix this in a follow-up commit if you like. https://reviews.llvm.org/D42672 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ added inline comments. Comment at: lib/Analysis/CFG.cpp:3899 + if (auto *CE = const_cast(NE->getConstructExpr())) +CurrentConstructionContext = {/*Constructor=*/CE, /*Trigger=*/NE}; + NoQ wrote: > dcoughlin wrote: > > Is it possible that there is already a CurrentConstructionContext? Will > > this overwrite it? > > > > Can you write an assertion to make sure that this doesn't happen? > > > > Generally I would expect a context to have a well-defined start and end. I > > think we should be explicit about where we expect it to start and end so we > > can find violations of our expectations. > Yes, for now it'd be overwritten every time we meet a new constructor (this > wasn't the case in the first version of this revision). However, the > additional check (which should be eventually replaced with an assertion, but > is for now needed) that we're picking the right context in `CXXConstructExpr` > visit, together with only visiting every constructor once during CFG > construction during normal visits (because weird stuff like > `CXXDefaultArgExpr` isn't yet supported), guarantees that we're not doing > anything wrong. > > I should have cleaned this up, but i don't want to invest attention into that > because subsequent patches will clearly make things way more complex than > that, whenever we start dealing with a multitude of construction contexts or > with partially-constructed contexts. > > So for now it's a trivial kinda-safe solution. > > I should document that though, for sure. Fixed now - added the `VisitForConstructionContext()` wrapper which contains the assertion that checks that we're not overwriting any existing context. The context is being cleaned up after it's consumed in `appendConstructor()`. https://reviews.llvm.org/D42672 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ updated this revision to Diff 132637. NoQ added a comment. Whoops! Actually use the new function with the assertion. Also disable creating construction contexts when the flag is off. https://reviews.llvm.org/D42672 Files: include/clang/Analysis/AnalysisDeclContext.h include/clang/Analysis/CFG.h include/clang/StaticAnalyzer/Core/AnalyzerOptions.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/Analysis/AnalysisDeclContext.cpp lib/Analysis/CFG.cpp lib/Analysis/LiveVariables.cpp lib/StaticAnalyzer/Core/AnalysisManager.cpp lib/StaticAnalyzer/Core/AnalyzerOptions.cpp lib/StaticAnalyzer/Core/CoreEngine.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/PathDiagnostic.cpp test/Analysis/analyzer-config.c test/Analysis/analyzer-config.cpp test/Analysis/cfg-rich-constructors.cpp test/Analysis/cfg.cpp Index: test/Analysis/cfg.cpp === --- test/Analysis/cfg.cpp +++ 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 @@ // 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 @@ // 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 @@ // 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 @@ // 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 Index: test/Analysis/cfg-rich-constructors.cpp === --- /dev/null +++ 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])
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ updated this revision to Diff 132532. NoQ marked an inline comment as done. NoQ added a comment. Cleanup construction contexts after they have been consumed. Add assertions to verify that our understanding of the context's lifespan is reasonable. I will run the new mode on a large codebase before committing, to see if these assertions ever fail or if i'm breaking anything else. Prettify ConstructionContext into a class with getters, write explicit constructors as pointed out in other patches. https://reviews.llvm.org/D42672 Files: include/clang/Analysis/AnalysisDeclContext.h include/clang/Analysis/CFG.h include/clang/StaticAnalyzer/Core/AnalyzerOptions.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/Analysis/AnalysisDeclContext.cpp lib/Analysis/CFG.cpp lib/Analysis/LiveVariables.cpp lib/StaticAnalyzer/Core/AnalysisManager.cpp lib/StaticAnalyzer/Core/AnalyzerOptions.cpp lib/StaticAnalyzer/Core/CoreEngine.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/PathDiagnostic.cpp test/Analysis/analyzer-config.c test/Analysis/analyzer-config.cpp test/Analysis/cfg-rich-constructors.cpp test/Analysis/cfg.cpp Index: test/Analysis/cfg.cpp === --- test/Analysis/cfg.cpp +++ 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 @@ // 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 @@ // 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 @@ // 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 @@ // 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 Index: test/Analysis/cfg-rich-constructors.cpp === --- /dev/null +++ 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(); +
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ updated this revision to Diff 132520. NoQ added a comment. Add more targeted tests. https://reviews.llvm.org/D42672 Files: include/clang/Analysis/AnalysisDeclContext.h include/clang/Analysis/CFG.h include/clang/StaticAnalyzer/Core/AnalyzerOptions.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/Analysis/AnalysisDeclContext.cpp lib/Analysis/CFG.cpp lib/Analysis/LiveVariables.cpp lib/StaticAnalyzer/Core/AnalysisManager.cpp lib/StaticAnalyzer/Core/AnalyzerOptions.cpp lib/StaticAnalyzer/Core/CoreEngine.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/PathDiagnostic.cpp test/Analysis/analyzer-config.c test/Analysis/analyzer-config.cpp test/Analysis/cfg-rich-constructors.cpp test/Analysis/cfg.cpp Index: test/Analysis/cfg.cpp === --- test/Analysis/cfg.cpp +++ 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 @@ // 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 @@ // 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 @@ // 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 @@ // 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 Index: test/Analysis/cfg-rich-constructors.cpp === --- /dev/null +++ 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
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ added inline comments. Comment at: lib/Analysis/CFG.cpp:3899 + if (auto *CE = const_cast(NE->getConstructExpr())) +CurrentConstructionContext = {/*Constructor=*/CE, /*Trigger=*/NE}; + dcoughlin wrote: > Is it possible that there is already a CurrentConstructionContext? Will this > overwrite it? > > Can you write an assertion to make sure that this doesn't happen? > > Generally I would expect a context to have a well-defined start and end. I > think we should be explicit about where we expect it to start and end so we > can find violations of our expectations. Yes, for now it'd be overwritten every time we meet a new constructor (this wasn't the case in the first version of this revision). However, the additional check (which should be eventually replaced with an assertion, but is for now needed) that we're picking the right context in `CXXConstructExpr` visit, together with only visiting every constructor once during CFG construction during normal visits (because weird stuff like `CXXDefaultArgExpr` isn't yet supported), guarantees that we're not doing anything wrong. I should have cleaned this up, but i don't want to invest attention into that because subsequent patches will clearly make things way more complex than that, whenever we start dealing with a multitude of construction contexts or with partially-constructed contexts. So for now it's a trivial kinda-safe solution. I should document that though, for sure. Comment at: lib/Analysis/CFG.cpp:4388 +case Stmt::WhileStmtClass: { + const VarDecl *var = cast(stmt)->getConditionVariable(); + if (var) dcoughlin wrote: > Please, let's try to avoid changes that are unrelated to the functionality > being added. I decreased indent of this whole huge for() loop, so the blame is already screwed (but phabricator carefully hides that), so i think this is a valid part of the patch. Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:632 + +void ExprEngine::ProcessConstructor(const CFGConstructor C, +ExplodedNode *Pred) { dcoughlin wrote: > You don't seem to be using the constructor context for anything other than > getting the CXXConstructExpr. Will a later patch require having a > CFGConstructor here? > > This seems like unnecessarily duplicated code. Can we just change ProcessStmt > to take a `Stmt *` rather than a CFGStmt and use it for all statements > including constructors? > Hmm, well, yeah, i guess, there isn't much value in passing the `CFGElement` around as long as we can find the current `CFGElement` any time we want in `ExprEngine->currBldrCtx`. But if not for that, it was the whole point to have the construction context there. Will fix. https://reviews.llvm.org/D42672 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ updated this revision to Diff 132476. NoQ marked 5 inline comments as done. NoQ added a comment. Address comments. Most importantly, separate out sema and analyzer CFG tests. https://reviews.llvm.org/D42672 Files: include/clang/Analysis/AnalysisDeclContext.h include/clang/Analysis/CFG.h include/clang/StaticAnalyzer/Core/AnalyzerOptions.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/Analysis/AnalysisDeclContext.cpp lib/Analysis/CFG.cpp lib/Analysis/LiveVariables.cpp lib/StaticAnalyzer/Core/AnalysisManager.cpp lib/StaticAnalyzer/Core/AnalyzerOptions.cpp lib/StaticAnalyzer/Core/CoreEngine.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/PathDiagnostic.cpp test/Analysis/analyzer-config.c test/Analysis/analyzer-config.cpp test/Analysis/cfg.cpp Index: test/Analysis/cfg.cpp === --- test/Analysis/cfg.cpp +++ test/Analysis/cfg.cpp @@ -1,5 +1,12 @@ -// 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). // CHECK-LABEL: void checkWrap(int i) // CHECK: ENTRY @@ -116,7 +123,8 @@ // 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 +146,8 @@ // 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 +340,8 @@ // 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 +373,8 @@ // 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 Index: test/Analysis/analyzer-config.cpp === --- test/Analysis/analyzer-config.cpp +++ test/Analysis/analyzer-config.cpp @@ -26,6 +26,7 @@ // 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: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -42,4 +43,4 @@ // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 24 +// CHECK-NEXT: num-entries = 25 Index: test/Analysis/analyzer-config.c === --- test/Analysis/analyzer-config.c +++ test/Analysis/analyzer-config.c @@ -15,6 +15,7 @@ // 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: faux-bodies = true // CHECK-NEXT:
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
dcoughlin added a comment. I think it will be great to have a more explicit representation of construction in the CFG! Comments in line. Comment at: include/clang/Analysis/CFG.h:143 + CXXConstructExpr *Constructor; + Stmt *Trigger; + I think it would be good to add a comment saying what Trigger is. Comment at: include/clang/Analysis/CFG.h:155 +/// constructor expression. +class CFGConstructor : public CFGElement { +public: Can you add a comment saying that this is only used by the analyzer's CFG? Comment at: lib/Analysis/CFG.cpp:475 + ConstructionContext CurrentConstructionContext = {nullptr, nullptr}; + A comment here would be helpful to future maintainers. For example, what does it mean for a construction context to be "Current"? Comment at: lib/Analysis/CFG.cpp:3899 + if (auto *CE = const_cast(NE->getConstructExpr())) +CurrentConstructionContext = {/*Constructor=*/CE, /*Trigger=*/NE}; + Is it possible that there is already a CurrentConstructionContext? Will this overwrite it? Can you write an assertion to make sure that this doesn't happen? Generally I would expect a context to have a well-defined start and end. I think we should be explicit about where we expect it to start and end so we can find violations of our expectations. Comment at: lib/Analysis/CFG.cpp:4388 +case Stmt::WhileStmtClass: { + const VarDecl *var = cast(stmt)->getConditionVariable(); + if (var) Please, let's try to avoid changes that are unrelated to the functionality being added. Comment at: lib/StaticAnalyzer/Core/AnalysisManager.cpp:32 /*addCXXNewAllocator=*/true, +/*addRichCXXConstructors=*/true, injector), I think this really needs to be a flag in AnalyzerOptions so you can keep the existing CFG tests for the behavior that other clients of the CFG rely on and add new tests with this flag explicitly set. Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:632 + +void ExprEngine::ProcessConstructor(const CFGConstructor C, +ExplodedNode *Pred) { You don't seem to be using the constructor context for anything other than getting the CXXConstructExpr. Will a later patch require having a CFGConstructor here? This seems like unnecessarily duplicated code. Can we just change ProcessStmt to take a `Stmt *` rather than a CFGStmt and use it for all statements including constructors? https://reviews.llvm.org/D42672 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D42672: [CFG] [analyzer] Heavier CFGConstructor elements.
NoQ updated this revision to Diff 132053. NoQ added a comment. Remove the stack of contexts for now. We're not using it anywhere yet, and i'm not sure if it'd be necessary. https://reviews.llvm.org/D42672 Files: include/clang/Analysis/AnalysisDeclContext.h include/clang/Analysis/CFG.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/Analysis/AnalysisDeclContext.cpp lib/Analysis/CFG.cpp lib/Analysis/LiveVariables.cpp lib/StaticAnalyzer/Core/AnalysisManager.cpp lib/StaticAnalyzer/Core/CoreEngine.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/PathDiagnostic.cpp test/Analysis/cfg.cpp Index: test/Analysis/cfg.cpp === --- test/Analysis/cfg.cpp +++ test/Analysis/cfg.cpp @@ -116,7 +116,7 @@ // CHECK-NEXT: Succs (1): B1 // CHECK: [B1] // CHECK-NEXT: 1: CFGNewAllocator(A *) -// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-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 +138,7 @@ // CHECK: [B1] // CHECK-NEXT: 1: 5 // CHECK-NEXT: 2: CFGNewAllocator(A *) -// CHECK-NEXT: 3: (CXXConstructExpr, class A [5]) +// CHECK-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 +331,7 @@ // 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) +// CHECK-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 +363,7 @@ // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, BitCast, void *) // CHECK-NEXT: 5: 5 // CHECK-NEXT: 6: CFGNewAllocator(MyClass *) -// CHECK-NEXT: 7: (CXXConstructExpr, class MyClass [5]) +// CHECK-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 Index: lib/StaticAnalyzer/Core/PathDiagnostic.cpp === --- lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -553,6 +553,9 @@ case CFGElement::Statement: return PathDiagnosticLocation(Source.castAs().getStmt(), SM, CallerCtx); + case CFGElement::Constructor: +return PathDiagnosticLocation( +Source.castAs().getConstructor(), SM, CallerCtx); case CFGElement::Initializer: { const CFGInitializer = Source.castAs(); return PathDiagnosticLocation(Init.getInitializer()->getInit(), Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp === --- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -209,7 +209,10 @@ // See if we're constructing an existing region by looking at the next // element in the CFG. const CFGBlock *B = CurrBldrCtx.getBlock(); - assert(isa(((*B)[currStmtIdx]).castAs().getStmt())); + // TODO: Retrieve construction target from CFGConstructor directly. + assert( + (*B)[currStmtIdx].getAs() || + isa(((*B)[currStmtIdx]).castAs().getStmt())); unsigned int NextStmtIdx = currStmtIdx + 1; if (NextStmtIdx >= B->size()) return None; @@ -250,10 +253,14 @@ Previous = (*B)[PreviousStmtIdx]; } - if (Optional PrevStmtElem = Previous.getAs()) { + if (auto PrevStmtElem = Previous.getAs()) { if (auto *CtorExpr = dyn_cast(PrevStmtElem->getStmt())) { return CtorExpr; } + } else if (auto PrevCtorElem = Previous.getAs()) { +if (auto *CtorExpr = PrevCtorElem->getConstructor()) { + return CtorExpr; +} } return nullptr; Index: lib/StaticAnalyzer/Core/ExprEngine.cpp === --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -454,10 +454,13 @@ switch (E.getKind()) { case CFGElement::Statement: - ProcessStmt(const_cast(E.castAs().getStmt()), Pred); + ProcessStmt(E.castAs(), Pred); + return; +case CFGElement::Constructor: + ProcessConstructor(E.castAs(), Pred); return; case CFGElement::Initializer: - ProcessInitializer(E.castAs().getInitializer(), Pred); + ProcessInitializer(E.castAs(), Pred); return; case CFGElement::NewAllocator: ProcessNewAllocator(E.castAs().getAllocatorExpr(), @@ -479,7 +482,7 @@ } static bool shouldRemoveDeadBindings(AnalysisManager , -