[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2018-01-17 Thread Phabricator via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL322774: [analyzer] operator new: Use the correct region for 
the constructor. (authored by dergachev, committed by ).
Herald added a subscriber: llvm-commits.

Changed prior to commit:
  https://reviews.llvm.org/D40560?vs=130249=130276#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D40560

Files:
  cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
  cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  cfe/trunk/test/Analysis/inline.cpp
  cfe/trunk/test/Analysis/new-ctor-conservative.cpp
  cfe/trunk/test/Analysis/new-ctor-inlined.cpp
  cfe/trunk/test/Analysis/new-ctor-recursive.cpp
  cfe/trunk/test/Analysis/new-ctor-symbolic.cpp

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
@@ -642,8 +642,8 @@
   const CXXConstructExpr *findDirectConstructorForCurrentCFGElement();
 
   /// For a CXXConstructExpr, walk forward in the current CFG block to find the
-  /// CFGElement for the DeclStmt or CXXInitCtorInitializer for which is
-  /// directly constructed by this constructor. Returns None if the current
+  /// CFGElement for the DeclStmt or CXXInitCtorInitializer or CXXNewExpr which
+  /// is directly constructed by this constructor. Returns None if the current
   /// constructor expression did not directly construct into an existing
   /// region.
   Optional findElementDirectlyInitializedByCurrentConstructor();
@@ -655,6 +655,30 @@
   /// if not.
   const MemRegion *getRegionForConstructedObject(const CXXConstructExpr *CE,
  ExplodedNode *Pred);
+
+  /// Store the region returned by operator new() so that the constructor
+  /// that follows it knew what location to initialize. The value should be
+  /// cleared once the respective CXXNewExpr CFGStmt element is processed.
+  static ProgramStateRef
+  setCXXNewAllocatorValue(ProgramStateRef State, const CXXNewExpr *CNE,
+  const LocationContext *CallerLC, SVal V);
+
+  /// Retrieve the location returned by the current operator new().
+  static SVal
+  getCXXNewAllocatorValue(ProgramStateRef State, const CXXNewExpr *CNE,
+  const LocationContext *CallerLC);
+
+  /// Clear the location returned by the respective operator new(). This needs
+  /// to be done as soon as CXXNewExpr CFG block is evaluated.
+  static ProgramStateRef
+  clearCXXNewAllocatorValue(ProgramStateRef State, const CXXNewExpr *CNE,
+const LocationContext *CallerLC);
+
+  /// Check if all allocator values are clear for the given context range
+  /// (including FromLC, not including ToLC). This is useful for assertions.
+  static bool areCXXNewAllocatorValuesClear(ProgramStateRef State,
+const LocationContext *FromLC,
+const LocationContext *ToLC);
 };
 
 /// Traits for storing the call processing policy inside GDM.
Index: cfe/trunk/test/Analysis/new-ctor-inlined.cpp
===
--- cfe/trunk/test/Analysis/new-ctor-inlined.cpp
+++ cfe/trunk/test/Analysis/new-ctor-inlined.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+void *operator new(size_t size) throw() {
+  void *x = conjure();
+  if (x == 0)
+exit(1);
+  return x;
+}
+
+struct S {
+  int x;
+  S() : x(1) {}
+  ~S() {}
+};
+
+void checkNewAndConstructorInlining() {
+  S *s = new S;
+  // Check that the symbol for 's' is not dying.
+  clang_analyzer_eval(s != 0); // expected-warning{{TRUE}}
+  // Check that bindings are correct (and also not dying).
+  clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}}
+}
+
+struct Sp {
+  Sp *p;
+  Sp(Sp *p): p(p) {}
+  ~Sp() {}
+};
+
+void checkNestedNew() {
+  Sp *p = new Sp(new Sp(0));
+  clang_analyzer_eval(p->p->p == 0); // expected-warning{{TRUE}}
+}
Index: cfe/trunk/test/Analysis/new-ctor-conservative.cpp
===
--- cfe/trunk/test/Analysis/new-ctor-conservative.cpp
+++ cfe/trunk/test/Analysis/new-ctor-conservative.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+
+struct S {
+  int x;
+  S() : x(1) {}
+  ~S() {}
+};
+
+void 

[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2018-01-17 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 130249.
NoQ added a comment.

Remove pointer casts from the newly added symbolic index test, because once we 
properly perform the cast of the allocated value (as in 
https://reviews.llvm.org/D41250), our solver would blow up.


https://reviews.llvm.org/D40560

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  test/Analysis/inline.cpp
  test/Analysis/new-ctor-conservative.cpp
  test/Analysis/new-ctor-inlined.cpp
  test/Analysis/new-ctor-recursive.cpp
  test/Analysis/new-ctor-symbolic.cpp

Index: test/Analysis/new-ctor-symbolic.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-symbolic.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_warnOnDeadSymbol(int);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+int conjure();
+void exit(int);
+
+struct S {
+  S() {}
+  ~S() {}
+
+  static S buffer[1000];
+
+  // This operator allocates stuff within the buffer. Additionally, it never
+  // places anything at the beginning of the buffer.
+  void *operator new(size_t size) {
+int i = conjure();
+if (i == 0)
+  exit(1);
+// Let's see if the symbol dies before new-expression is evaluated.
+// It shouldn't.
+clang_analyzer_warnOnDeadSymbol(i);
+return buffer + i;
+  }
+};
+
+void testIndexLiveness() {
+  S *s = new S();
+  clang_analyzer_eval(s == S::buffer); // expected-warning{{FALSE}}
+} // expected-warning{{SYMBOL DEAD}}
Index: test/Analysis/new-ctor-recursive.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-recursive.cpp
@@ -0,0 +1,118 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_dump(int);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+struct S;
+
+S *global_s;
+
+// Recursive operator kinda placement new.
+void *operator new(size_t size, S *place);
+
+enum class ConstructionKind : char {
+  Garbage,
+  Recursive
+};
+
+struct S {
+public:
+  int x;
+  S(): x(1) {}
+  S(int y): x(y) {}
+
+  S(ConstructionKind k) {
+switch (k) {
+case ConstructionKind::Recursive: { // Call one more operator new 'r'ecursively.
+  S *s = new (nullptr) S(5);
+  x = s->x + 1;
+  global_s = s;
+  return;
+}
+case ConstructionKind::Garbage: {
+  // Leaves garbage in 'x'.
+}
+}
+  }
+  ~S() {}
+};
+
+// Do not try this at home!
+void *operator new(size_t size, S *place) {
+  if (!place)
+return new S();
+  return place;
+}
+
+void testThatCharConstructorIndeedYieldsGarbage() {
+  S *s = new S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(s->x == 1); // expected-warning{{UNKNOWN}}
+  // FIXME: This should warn, but MallocChecker doesn't default-bind regions
+  // returned by standard operator new to garbage.
+  s->x += 1; // no-warning
+  delete s;
+}
+
+
+void testChainedOperatorNew() {
+  S *s;
+  // * Evaluate standard new.
+  // * Evaluate constructor S(3).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (new S(3)) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 3); // expected-warning{{TRUE}}
+  // expected-warning@+9{{Potential leak of memory pointed to by 's'}}
+
+  // * Evaluate standard new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(4).
+  // * Bind value for our custom new.
+  s = new (new S(ConstructionKind::Garbage)) S(4);
+  clang_analyzer_eval(s->x == 4); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (nullptr) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // -> Enter constructor S(Recursive).
+  //   -> Enter our custom new (nullptr).
+  // * Evaluate standard new.
+  // * Inline constructor S().
+  // * Bind 

[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2018-01-17 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:487
+if (const MemRegion *MR = I.second.getAsRegion())
+  SymReaper.markElementIndicesLive(MR);
+  }

dcoughlin wrote:
> Do we have a test for the MemRegion case? Commenting it out doesn't seem to 
> affect the tests.
Right, added one (`new-ctor-symbolic.cpp`).


https://reviews.llvm.org/D40560



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2018-01-17 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 130243.
NoQ marked 2 inline comments as done.
NoQ added a comment.

Address the final comments.


https://reviews.llvm.org/D40560

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  test/Analysis/inline.cpp
  test/Analysis/new-ctor-conservative.cpp
  test/Analysis/new-ctor-inlined.cpp
  test/Analysis/new-ctor-recursive.cpp
  test/Analysis/new-ctor-symbolic.cpp

Index: test/Analysis/new-ctor-symbolic.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-symbolic.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_warnOnDeadSymbol(int);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+int conjure();
+void exit(int);
+
+char buffer[1000];
+
+// This operator allocates stuff within the buffer. Additionally, it never
+// places anything at the beginning of the buffer.
+void *operator new(size_t size) {
+  int i = conjure();
+  if (i == 0)
+exit(1);
+  // Let's see if the symbol dies before new-expression is evaluated.
+  // It shouldn't.
+  clang_analyzer_warnOnDeadSymbol(i);
+  return buffer + i;
+}
+
+struct S {
+S() {}
+~S() {}
+};
+
+void testIndexLiveness() {
+  S *s = new S();
+  clang_analyzer_eval((void *)s == (void *)buffer); // expected-warning{{FALSE}}
+} // expected-warning{{SYMBOL DEAD}}
Index: test/Analysis/new-ctor-recursive.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-recursive.cpp
@@ -0,0 +1,118 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_dump(int);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+struct S;
+
+S *global_s;
+
+// Recursive operator kinda placement new.
+void *operator new(size_t size, S *place);
+
+enum class ConstructionKind : char {
+  Garbage,
+  Recursive
+};
+
+struct S {
+public:
+  int x;
+  S(): x(1) {}
+  S(int y): x(y) {}
+
+  S(ConstructionKind k) {
+switch (k) {
+case ConstructionKind::Recursive: { // Call one more operator new 'r'ecursively.
+  S *s = new (nullptr) S(5);
+  x = s->x + 1;
+  global_s = s;
+  return;
+}
+case ConstructionKind::Garbage: {
+  // Leaves garbage in 'x'.
+}
+}
+  }
+  ~S() {}
+};
+
+// Do not try this at home!
+void *operator new(size_t size, S *place) {
+  if (!place)
+return new S();
+  return place;
+}
+
+void testThatCharConstructorIndeedYieldsGarbage() {
+  S *s = new S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(s->x == 1); // expected-warning{{UNKNOWN}}
+  // FIXME: This should warn, but MallocChecker doesn't default-bind regions
+  // returned by standard operator new to garbage.
+  s->x += 1; // no-warning
+  delete s;
+}
+
+
+void testChainedOperatorNew() {
+  S *s;
+  // * Evaluate standard new.
+  // * Evaluate constructor S(3).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (new S(3)) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 3); // expected-warning{{TRUE}}
+  // expected-warning@+9{{Potential leak of memory pointed to by 's'}}
+
+  // * Evaluate standard new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(4).
+  // * Bind value for our custom new.
+  s = new (new S(ConstructionKind::Garbage)) S(4);
+  clang_analyzer_eval(s->x == 4); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (nullptr) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // -> Enter constructor S(Recursive).
+  //   -> Enter our custom new (nullptr).
+  // * Evaluate standard new.
+  // * Inline constructor S().
+  // * Bind value for standard new.
+  //   <- Exit our custom new (nullptr).
+  //   * Evaluate constructor S(5).
+  //   * Bind value for our custom new (nullptr).

[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2018-01-12 Thread Devin Coughlin via Phabricator via cfe-commits
dcoughlin accepted this revision.
dcoughlin added a comment.
This revision is now accepted and ready to land.

LGTM with the TODO and the test case I requested inline.




Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:487
+if (const MemRegion *MR = I.second.getAsRegion())
+  SymReaper.markElementIndicesLive(MR);
+  }

Do we have a test for the MemRegion case? Commenting it out doesn't seem to 
affect the tests.



Comment at: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp:607
+const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
+const Stmt *ParentExpr = CurLC->getParentMap().getParent(CtorExpr);
+

Can you add a TODO saying that we really shouldn't be using the parent map 
here? That is fragile and is a sign we're not providing enough context.



https://reviews.llvm.org/D40560



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2018-01-11 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 129552.
NoQ added a comment.

Add a stronger assertion: when ending an inlined call, assert that no stale 
allocator values remain.


https://reviews.llvm.org/D40560

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  test/Analysis/inline.cpp
  test/Analysis/new-ctor-conservative.cpp
  test/Analysis/new-ctor-inlined.cpp
  test/Analysis/new-ctor-recursive.cpp

Index: test/Analysis/new-ctor-recursive.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-recursive.cpp
@@ -0,0 +1,118 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_dump(int);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+struct S;
+
+S *global_s;
+
+// Recursive operator kinda placement new.
+void *operator new(size_t size, S *place);
+
+enum class ConstructionKind : char {
+  Garbage,
+  Recursive
+};
+
+struct S {
+public:
+  int x;
+  S(): x(1) {}
+  S(int y): x(y) {}
+
+  S(ConstructionKind k) {
+switch (k) {
+case ConstructionKind::Recursive: { // Call one more operator new 'r'ecursively.
+  S *s = new (nullptr) S(5);
+  x = s->x + 1;
+  global_s = s;
+  return;
+}
+case ConstructionKind::Garbage: {
+  // Leaves garbage in 'x'.
+}
+}
+  }
+  ~S() {}
+};
+
+// Do not try this at home!
+void *operator new(size_t size, S *place) {
+  if (!place)
+return new S();
+  return place;
+}
+
+void testThatCharConstructorIndeedYieldsGarbage() {
+  S *s = new S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(s->x == 1); // expected-warning{{UNKNOWN}}
+  // FIXME: This should warn, but MallocChecker doesn't default-bind regions
+  // returned by standard operator new to garbage.
+  s->x += 1; // no-warning
+  delete s;
+}
+
+
+void testChainedOperatorNew() {
+  S *s;
+  // * Evaluate standard new.
+  // * Evaluate constructor S(3).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (new S(3)) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 3); // expected-warning{{TRUE}}
+  // expected-warning@+9{{Potential leak of memory pointed to by 's'}}
+
+  // * Evaluate standard new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(4).
+  // * Bind value for our custom new.
+  s = new (new S(ConstructionKind::Garbage)) S(4);
+  clang_analyzer_eval(s->x == 4); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (nullptr) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // -> Enter constructor S(Recursive).
+  //   -> Enter our custom new (nullptr).
+  // * Evaluate standard new.
+  // * Inline constructor S().
+  // * Bind value for standard new.
+  //   <- Exit our custom new (nullptr).
+  //   * Evaluate constructor S(5).
+  //   * Bind value for our custom new (nullptr).
+  //   * Assign that value to global_s.
+  // <- Exit constructor S(Recursive).
+  // * Bind value for our custom new (nullptr).
+  global_s = nullptr;
+  s = new (nullptr) S(ConstructionKind::Recursive);
+  clang_analyzer_eval(global_s); // expected-warning{{TRUE}}
+  clang_analyzer_eval(global_s->x == 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval(s->x == 6); // expected-warning{{TRUE}}
+  delete s;
+}
Index: test/Analysis/new-ctor-inlined.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-inlined.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+void *operator new(size_t size) throw() {
+  void *x = conjure();
+  if (x == 0)
+exit(1);
+  return x;
+}
+
+struct S {
+  int x;
+  S() : x(1) {}
+  ~S() {}
+};
+
+void checkNewAndConstructorInlining() {
+  S *s = new 

[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2018-01-10 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 129362.
NoQ added a comment.

Rename getters and setters for the new state trait (i.e. "push" -> "set", etc., 
because we no longer have a stack).

Also make them static, so that it was potentially possible to access them from 
elsewhere, eg. from `CallEvent`, if deemed necessary.


https://reviews.llvm.org/D40560

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  test/Analysis/inline.cpp
  test/Analysis/new-ctor-conservative.cpp
  test/Analysis/new-ctor-inlined.cpp
  test/Analysis/new-ctor-recursive.cpp

Index: test/Analysis/new-ctor-recursive.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-recursive.cpp
@@ -0,0 +1,118 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_dump(int);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+struct S;
+
+S *global_s;
+
+// Recursive operator kinda placement new.
+void *operator new(size_t size, S *place);
+
+enum class ConstructionKind : char {
+  Garbage,
+  Recursive
+};
+
+struct S {
+public:
+  int x;
+  S(): x(1) {}
+  S(int y): x(y) {}
+
+  S(ConstructionKind k) {
+switch (k) {
+case ConstructionKind::Recursive: { // Call one more operator new 'r'ecursively.
+  S *s = new (nullptr) S(5);
+  x = s->x + 1;
+  global_s = s;
+  return;
+}
+case ConstructionKind::Garbage: {
+  // Leaves garbage in 'x'.
+}
+}
+  }
+  ~S() {}
+};
+
+// Do not try this at home!
+void *operator new(size_t size, S *place) {
+  if (!place)
+return new S();
+  return place;
+}
+
+void testThatCharConstructorIndeedYieldsGarbage() {
+  S *s = new S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(s->x == 1); // expected-warning{{UNKNOWN}}
+  // FIXME: This should warn, but MallocChecker doesn't default-bind regions
+  // returned by standard operator new to garbage.
+  s->x += 1; // no-warning
+  delete s;
+}
+
+
+void testChainedOperatorNew() {
+  S *s;
+  // * Evaluate standard new.
+  // * Evaluate constructor S(3).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (new S(3)) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 3); // expected-warning{{TRUE}}
+  // expected-warning@+9{{Potential leak of memory pointed to by 's'}}
+
+  // * Evaluate standard new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(4).
+  // * Bind value for our custom new.
+  s = new (new S(ConstructionKind::Garbage)) S(4);
+  clang_analyzer_eval(s->x == 4); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (nullptr) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // -> Enter constructor S(Recursive).
+  //   -> Enter our custom new (nullptr).
+  // * Evaluate standard new.
+  // * Inline constructor S().
+  // * Bind value for standard new.
+  //   <- Exit our custom new (nullptr).
+  //   * Evaluate constructor S(5).
+  //   * Bind value for our custom new (nullptr).
+  //   * Assign that value to global_s.
+  // <- Exit constructor S(Recursive).
+  // * Bind value for our custom new (nullptr).
+  global_s = nullptr;
+  s = new (nullptr) S(ConstructionKind::Recursive);
+  clang_analyzer_eval(global_s); // expected-warning{{TRUE}}
+  clang_analyzer_eval(global_s->x == 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval(s->x == 6); // expected-warning{{TRUE}}
+  delete s;
+}
Index: test/Analysis/new-ctor-inlined.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-inlined.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+void *operator new(size_t size) throw() {
+  void *x = conjure();
+  if 

[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2018-01-09 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 129210.
NoQ added a comment.

That thing didn't immediately work, because there are a lot of other places 
where we need to put the value, not just the Store, before entering the 
constructor - such as our constructor call events for checker callbacks. It'd 
be hard for the call event to extract the target region by looking at their 
caller stack frame and program state, and perhaps they shouldn't be doing this, 
and it's actually fine that they receive the target region directly, because if 
we want to reconstruct the call event after the fact, we'd anyway be able to do 
this only from within the constructor call, because later the value would 
disappear from the program state anyway.

The idea with the new location context class still stands. For now i made a 
simple map from (`CallerStackFrameContext`, `CXXNewExpr`) pairs to `SVal`. This 
map can be trivially refactored into a map from `OurNewLocationContext` to 
`SVal`, because `CallerStackFrame` would be its parent context, and 
`CXXNewExpr` would be its parameter. Note that it's not possible to use only 
`CallerStackFrameContext` as the key because multiple `CXXNewExpr`s might be 
active simultaneously, eg. `new X(new Y())` - respective test case added. But 
with `CXXNewExpr` as part of the key, the key is indeed unique in the sense 
that by the time we encounter the same `CXXNewExpr` again we'd be done with the 
old `CXXNewExpr` - respective assertion added. With these assertions i guess 
it's more reliable than the stack approach.

I think i'm getting done with these patches, so they can be treated as in sort 
of final shape, i.e. i have no planned changes for these myself anymore (but 
i'd definitely gladly address any review comments).


https://reviews.llvm.org/D40560

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  test/Analysis/inline.cpp
  test/Analysis/new-ctor-conservative.cpp
  test/Analysis/new-ctor-inlined.cpp
  test/Analysis/new-ctor-recursive.cpp

Index: test/Analysis/new-ctor-recursive.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-recursive.cpp
@@ -0,0 +1,118 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_dump(int);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+struct S;
+
+S *global_s;
+
+// Recursive operator kinda placement new.
+void *operator new(size_t size, S *place);
+
+enum class ConstructionKind : char {
+  Garbage,
+  Recursive
+};
+
+struct S {
+public:
+  int x;
+  S(): x(1) {}
+  S(int y): x(y) {}
+
+  S(ConstructionKind k) {
+switch (k) {
+case ConstructionKind::Recursive: { // Call one more operator new 'r'ecursively.
+  S *s = new (nullptr) S(5);
+  x = s->x + 1;
+  global_s = s;
+  return;
+}
+case ConstructionKind::Garbage: {
+  // Leaves garbage in 'x'.
+}
+}
+  }
+  ~S() {}
+};
+
+// Do not try this at home!
+void *operator new(size_t size, S *place) {
+  if (!place)
+return new S();
+  return place;
+}
+
+void testThatCharConstructorIndeedYieldsGarbage() {
+  S *s = new S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(s->x == 1); // expected-warning{{UNKNOWN}}
+  // FIXME: This should warn, but MallocChecker doesn't default-bind regions
+  // returned by standard operator new to garbage.
+  s->x += 1; // no-warning
+  delete s;
+}
+
+
+void testChainedOperatorNew() {
+  S *s;
+  // * Evaluate standard new.
+  // * Evaluate constructor S(3).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (new S(3)) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 3); // expected-warning{{TRUE}}
+  // expected-warning@+9{{Potential leak of memory pointed to by 's'}}
+
+  // * Evaluate standard new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(4).
+  // * Bind value for our custom new.
+  s = new (new S(ConstructionKind::Garbage)) S(4);
+  clang_analyzer_eval(s->x == 4); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (nullptr) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}}
+  delete s;

[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2017-12-20 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

A slower explanation of the approach in '3.' in the previous message:

(1) Evaluate operator new() aka the allocator call as usual.
(2) Take the return value of (1) as usual.
(3) Take `CXXConstructExpr` which is the child of the `CXXNewExpr` that 
triggered the allocator call on (1).
(4) Construct a `StackFrameContext` with `CXXConstructExpr` from (3).
(5) //**Don't**// put the newly constructed `StackFrameContext` on the location 
context stack.
(6) Construct the `StackArgumentsSpaceRegion` for the `StackFrameContext` from 
(4).
(7) Construct the `CXXThisRegion` for the `StackArgumentsSpaceRegion` from (6).
(8) Bind the return value from (2) to `CXXThisRegion` from (7) in the Store.
(9) Put the node with the state from (8) to the worklist as usual.
(10) `CoreEngine` says it's time to evaluate `CXXConstructExpr` from (3) as 
usual.
(11) Make sure that the binding we made in (8) survives garbage collection*.
(11) Construct `StackFrameContext` for the `CXXConstructExpr` from (3) as usual.
(12) `LocationContextManager` ensures that on (4) and on (11) we get //the 
same// `StackFrameContext`.
(13) //**Don't**// bind `CXXThisRegion` while entering the stack frame - it was 
already done in (8).
(14) Finally enter the stack frame we've constructed twice on (4) and on (11), 
as usual.
(15) Evaluate the constructor, as usual.
(16) Bind this-value to `CXXConstructExpr` after evaluation (as usual? not 
sure).
(17) Allow the binding in the Store we made on (8) to be garbage-colllected as 
usual.
(18) When evaluating `CXXNewExpr`, take value of `CXXConstructExpr` and bind it 
to `CXXNewExpr`.

__
*We  may modify `SymbolReaper::isLiveRegion()` for this purpose. Sounds easy.


https://reviews.llvm.org/D40560



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2017-12-20 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

1. Devin suggested a fantastic idea: it may be great to have a new 
`LocationContext` for whatever happens within the big-new-expression. This 
would help immensely with CFG look-aheads and look-behinds and reduce parent 
map usage - not only for operator new, but for other constructors (into 
initializer lists, probably into function arguments). Let's call it 
`ConstructionTriggerContext` for now. We can also store our "`_this`" value in 
a program state map from `ConstructionTriggerContext` into `SVal`.

2. My reaction was that we can instead store our "`_this`" value in some sort 
of "`CXX_ThisRegion`" within the `StackLocalsSpaceRegion` that corresponds to 
`ConstructionTriggerContext`, and then copy it over to `CXXThisRegion` that 
corresponds to the `StackFrameContext` of the constructor. This would kinda 
make sense given the pseudocode that we're imagine here for the new-expression. 
However, this sounds a bit over-engineered because we're using the Store to 
just temporarily stash the value. Well, right now we're using Environment for 
that, but it's equally dirty.

3. Now, it might actually be better to store "`_this`" value directly into 
`CXXThisRegion` that corresponds to the `StackFrameContext` of the constructor 
(rather than stash it in another place and eventually copy), even if that stack 
frame has not been entered yet. Because the stack frame would anyway be entered 
almost immediately, we already know the parameters of the stack frame (parent 
location context, call site, block count, block id, element id), and location 
contexts  are uniqued, so we can add the store binding now to the stack frame 
of the future, and whenever we actually enter the stack frame, when formerly 
we'd have bound the value to `CXXThisRegion`, we'd see that the value is 
already there. In this case we don't immediately need 
`ConstructionTriggerContext` - we can still add it later to refactor 
look-aheads and stuff. The binding would anyway be garbage-collected once the 
constructor exits.

I'd be glad to implement approach 3. instead of the stack of values if it 
sounds reasonable.


https://reviews.llvm.org/D40560



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2017-12-18 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ marked 2 inline comments as done.
NoQ added a comment.

Sorry for the spam.


https://reviews.llvm.org/D40560



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2017-12-18 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 127437.
NoQ added a comment.

- Fix more `for(:)` whitespace.


https://reviews.llvm.org/D40560

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  test/Analysis/inline.cpp
  test/Analysis/new-ctor-conservative.cpp
  test/Analysis/new-ctor-inlined.cpp
  test/Analysis/new-ctor-recursive.cpp

Index: test/Analysis/new-ctor-recursive.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-recursive.cpp
@@ -0,0 +1,118 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_dump(int);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+struct S;
+
+S *global_s;
+
+// Recursive operator kinda placement new.
+void *operator new(size_t size, S *place);
+
+enum class ConstructionKind : char {
+  Garbage,
+  Recursive
+};
+
+struct S {
+public:
+  int x;
+  S(): x(1) {}
+  S(int y): x(y) {}
+
+  S(ConstructionKind k) {
+switch (k) {
+case ConstructionKind::Recursive: { // Call one more operator new 'r'ecursively.
+  S *s = new (nullptr) S(5);
+  x = s->x + 1;
+  global_s = s;
+  return;
+}
+case ConstructionKind::Garbage: {
+  // Leaves garbage in 'x'.
+}
+}
+  }
+  ~S() {}
+};
+
+// Do not try this at home!
+void *operator new(size_t size, S *place) {
+  if (!place)
+return new S();
+  return place;
+}
+
+void testThatCharConstructorIndeedYieldsGarbage() {
+  S *s = new S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(s->x == 1); // expected-warning{{UNKNOWN}}
+  // FIXME: This should warn, but MallocChecker doesn't default-bind regions
+  // returned by standard operator new to garbage.
+  s->x += 1; // no-warning
+  delete s;
+}
+
+
+void testChainedOperatorNew() {
+  S *s;
+  // * Evaluate standard new.
+  // * Evaluate constructor S(3).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (new S(3)) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 3); // expected-warning{{TRUE}}
+  // expected-warning@+9{{Potential leak of memory pointed to by 's'}}
+
+  // * Evaluate standard new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(4).
+  // * Bind value for our custom new.
+  s = new (new S(ConstructionKind::Garbage)) S(4);
+  clang_analyzer_eval(s->x == 4); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (nullptr) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // -> Enter constructor S(Recursive).
+  //   -> Enter our custom new (nullptr).
+  // * Evaluate standard new.
+  // * Inline constructor S().
+  // * Bind value for standard new.
+  //   <- Exit our custom new (nullptr).
+  //   * Evaluate constructor S(5).
+  //   * Bind value for our custom new (nullptr).
+  //   * Assign that value to global_s.
+  // <- Exit constructor S(Recursive).
+  // * Bind value for our custom new (nullptr).
+  global_s = nullptr;
+  s = new (nullptr) S(ConstructionKind::Recursive);
+  clang_analyzer_eval(global_s); // expected-warning{{TRUE}}
+  clang_analyzer_eval(global_s->x == 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval(s->x == 6); // expected-warning{{TRUE}}
+  delete s;
+}
Index: test/Analysis/new-ctor-inlined.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-inlined.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+void *operator new(size_t size) throw() {
+  void *x = conjure();
+  if (x == 0)
+exit(1);
+  return x;
+}
+
+struct S {
+  int x;
+  S() : x(1) {}
+  ~S() {}
+};
+
+void checkNewAndConstructorInlining() {
+  S *s = new S;
+  // Check that the symbol for 's' is not dying.
+  

[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2017-12-18 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 127436.
NoQ added a comment.

- Actually fix the comment about why we need to act on null or undefined values.
- Fix `for(:)` whitespace.


https://reviews.llvm.org/D40560

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  test/Analysis/inline.cpp
  test/Analysis/new-ctor-conservative.cpp
  test/Analysis/new-ctor-inlined.cpp
  test/Analysis/new-ctor-recursive.cpp

Index: test/Analysis/new-ctor-recursive.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-recursive.cpp
@@ -0,0 +1,118 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_dump(int);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+struct S;
+
+S *global_s;
+
+// Recursive operator kinda placement new.
+void *operator new(size_t size, S *place);
+
+enum class ConstructionKind : char {
+  Garbage,
+  Recursive
+};
+
+struct S {
+public:
+  int x;
+  S(): x(1) {}
+  S(int y): x(y) {}
+
+  S(ConstructionKind k) {
+switch (k) {
+case ConstructionKind::Recursive: { // Call one more operator new 'r'ecursively.
+  S *s = new (nullptr) S(5);
+  x = s->x + 1;
+  global_s = s;
+  return;
+}
+case ConstructionKind::Garbage: {
+  // Leaves garbage in 'x'.
+}
+}
+  }
+  ~S() {}
+};
+
+// Do not try this at home!
+void *operator new(size_t size, S *place) {
+  if (!place)
+return new S();
+  return place;
+}
+
+void testThatCharConstructorIndeedYieldsGarbage() {
+  S *s = new S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(s->x == 1); // expected-warning{{UNKNOWN}}
+  // FIXME: This should warn, but MallocChecker doesn't default-bind regions
+  // returned by standard operator new to garbage.
+  s->x += 1; // no-warning
+  delete s;
+}
+
+
+void testChainedOperatorNew() {
+  S *s;
+  // * Evaluate standard new.
+  // * Evaluate constructor S(3).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (new S(3)) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 3); // expected-warning{{TRUE}}
+  // expected-warning@+9{{Potential leak of memory pointed to by 's'}}
+
+  // * Evaluate standard new.
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for standard new.
+  // * Evaluate our custom new.
+  // * Evaluate constructor S(4).
+  // * Bind value for our custom new.
+  s = new (new S(ConstructionKind::Garbage)) S(4);
+  clang_analyzer_eval(s->x == 4); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // * Evaluate constructor S(Garbage).
+  // * Bind value for our custom new.
+  s = new (nullptr) S(ConstructionKind::Garbage);
+  clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}}
+  delete s;
+
+  // -> Enter our custom new (nullptr).
+  //   * Evaluate standard new.
+  //   * Inline constructor S().
+  //   * Bind value for standard new.
+  // <- Exit our custom new (nullptr).
+  // -> Enter constructor S(Recursive).
+  //   -> Enter our custom new (nullptr).
+  // * Evaluate standard new.
+  // * Inline constructor S().
+  // * Bind value for standard new.
+  //   <- Exit our custom new (nullptr).
+  //   * Evaluate constructor S(5).
+  //   * Bind value for our custom new (nullptr).
+  //   * Assign that value to global_s.
+  // <- Exit constructor S(Recursive).
+  // * Bind value for our custom new (nullptr).
+  global_s = nullptr;
+  s = new (nullptr) S(ConstructionKind::Recursive);
+  clang_analyzer_eval(global_s); // expected-warning{{TRUE}}
+  clang_analyzer_eval(global_s->x == 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval(s->x == 6); // expected-warning{{TRUE}}
+  delete s;
+}
Index: test/Analysis/new-ctor-inlined.cpp
===
--- /dev/null
+++ test/Analysis/new-ctor-inlined.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *conjure();
+void exit(int);
+
+void *operator new(size_t size) throw() {
+  void *x = conjure();
+  if (x == 0)
+exit(1);
+  return x;
+}
+
+struct S {
+  int x;
+  S() : x(1) {}
+  ~S() {}
+};
+
+void checkNewAndConstructorInlining() {
+  S *s 

[PATCH] D40560: [analyzer] Get construction into `operator new` running in simple cases.

2017-12-18 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

I think this commit starts to make sense, so i removed the "WIP" marker. I 
guess it's better to leave todos 2 and 4 to follow-up commits, and 1 and 3 are 
already addressed.


https://reviews.llvm.org/D40560



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits