[PATCH] D50363: [analyzer] pr37204: Take signedness into account in BasicValueFactory::getTruthValue().

2018-08-06 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, mikhail.ramalho, baloghadamsoftware, 
eraman.

I don't see why BasicValueFactory::getTruthValue(bool, QualType) always returns 
an unsigned `APSInt` even if the `QualType` is signed, so i fixed it.

This was causing a couple of crashes, including pr37204.


Repository:
  rC Clang

https://reviews.llvm.org/D50363

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
  test/Analysis/casts.c
  test/Analysis/std-c-library-functions-inlined.c


Index: test/Analysis/std-c-library-functions-inlined.c
===
--- /dev/null
+++ test/Analysis/std-c-library-functions-inlined.c
@@ -0,0 +1,17 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=unix.StdCLibraryFunctions -verify 
%s
+// RUN: %clang_analyze_cc1 -triple i686-unknown-linux 
-analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux 
-analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple armv7-a15-linux 
-analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple thumbv7-a15-linux 
-analyzer-checker=unix.StdCLibraryFunctions -verify %s
+
+// This test tests crashes that occur when standard functions are available
+// for inlining.
+
+// expected-no-diagnostics
+
+int isdigit(int _) { return !0; }
+void test_redefined_isdigit(int x) {
+  int (*func)(int) = isdigit;
+  for (; func(x);) // no-crash
+;
+}
Index: test/Analysis/casts.c
===
--- test/Analysis/casts.c
+++ test/Analysis/casts.c
@@ -182,3 +182,9 @@
 c += 1;
   }
 }
+
+void testSwitchWithSizeofs() {
+  switch (sizeof(char) == 1) { // expected-warning{{switch condition has 
boolean value}}
+  case sizeof(char):; // no-crash
+  }
+}
Index: include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
@@ -211,7 +211,8 @@
   }
 
   const llvm::APSInt &getTruthValue(bool b, QualType T) {
-return getValue(b ? 1 : 0, Ctx.getIntWidth(T), true);
+return T, getValue(b ? 1 : 0, Ctx.getIntWidth(T),
+   T->isUnsignedIntegerOrEnumerationType());
   }
 
   const llvm::APSInt &getTruthValue(bool b) {


Index: test/Analysis/std-c-library-functions-inlined.c
===
--- /dev/null
+++ test/Analysis/std-c-library-functions-inlined.c
@@ -0,0 +1,17 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple i686-unknown-linux -analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux -analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple armv7-a15-linux -analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple thumbv7-a15-linux -analyzer-checker=unix.StdCLibraryFunctions -verify %s
+
+// This test tests crashes that occur when standard functions are available
+// for inlining.
+
+// expected-no-diagnostics
+
+int isdigit(int _) { return !0; }
+void test_redefined_isdigit(int x) {
+  int (*func)(int) = isdigit;
+  for (; func(x);) // no-crash
+;
+}
Index: test/Analysis/casts.c
===
--- test/Analysis/casts.c
+++ test/Analysis/casts.c
@@ -182,3 +182,9 @@
 c += 1;
   }
 }
+
+void testSwitchWithSizeofs() {
+  switch (sizeof(char) == 1) { // expected-warning{{switch condition has boolean value}}
+  case sizeof(char):; // no-crash
+  }
+}
Index: include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
@@ -211,7 +211,8 @@
   }
 
   const llvm::APSInt &getTruthValue(bool b, QualType T) {
-return getValue(b ? 1 : 0, Ctx.getIntWidth(T), true);
+return T, getValue(b ? 1 : 0, Ctx.getIntWidth(T),
+   T->isUnsignedIntegerOrEnumerationType());
   }
 
   const llvm::APSInt &getTruthValue(bool b) {
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50211: [analyzer] Fix displayed checker name for InnerPointerChecker

2018-08-06 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Welcome to the club!


https://reviews.llvm.org/D50211



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


[PATCH] D50363: [analyzer] pr37204: Take signedness into account in BasicValueFactory::getTruthValue().

2018-08-06 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 159435.
NoQ added a comment.

Whoops, how did this `T, ` thing get in there.


https://reviews.llvm.org/D50363

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
  test/Analysis/casts.c
  test/Analysis/std-c-library-functions-inlined.c


Index: test/Analysis/std-c-library-functions-inlined.c
===
--- /dev/null
+++ test/Analysis/std-c-library-functions-inlined.c
@@ -0,0 +1,17 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=unix.StdCLibraryFunctions -verify 
%s
+// RUN: %clang_analyze_cc1 -triple i686-unknown-linux 
-analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux 
-analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple armv7-a15-linux 
-analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple thumbv7-a15-linux 
-analyzer-checker=unix.StdCLibraryFunctions -verify %s
+
+// This test tests crashes that occur when standard functions are available
+// for inlining.
+
+// expected-no-diagnostics
+
+int isdigit(int _) { return !0; }
+void test_redefined_isdigit(int x) {
+  int (*func)(int) = isdigit;
+  for (; func(x);) // no-crash
+;
+}
Index: test/Analysis/casts.c
===
--- test/Analysis/casts.c
+++ test/Analysis/casts.c
@@ -182,3 +182,9 @@
 c += 1;
   }
 }
+
+void testSwitchWithSizeofs() {
+  switch (sizeof(char) == 1) { // expected-warning{{switch condition has 
boolean value}}
+  case sizeof(char):; // no-crash
+  }
+}
Index: include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
@@ -211,7 +211,8 @@
   }
 
   const llvm::APSInt &getTruthValue(bool b, QualType T) {
-return getValue(b ? 1 : 0, Ctx.getIntWidth(T), true);
+return getValue(b ? 1 : 0, Ctx.getIntWidth(T),
+T->isUnsignedIntegerOrEnumerationType());
   }
 
   const llvm::APSInt &getTruthValue(bool b) {


Index: test/Analysis/std-c-library-functions-inlined.c
===
--- /dev/null
+++ test/Analysis/std-c-library-functions-inlined.c
@@ -0,0 +1,17 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple i686-unknown-linux -analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux -analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple armv7-a15-linux -analyzer-checker=unix.StdCLibraryFunctions -verify %s
+// RUN: %clang_analyze_cc1 -triple thumbv7-a15-linux -analyzer-checker=unix.StdCLibraryFunctions -verify %s
+
+// This test tests crashes that occur when standard functions are available
+// for inlining.
+
+// expected-no-diagnostics
+
+int isdigit(int _) { return !0; }
+void test_redefined_isdigit(int x) {
+  int (*func)(int) = isdigit;
+  for (; func(x);) // no-crash
+;
+}
Index: test/Analysis/casts.c
===
--- test/Analysis/casts.c
+++ test/Analysis/casts.c
@@ -182,3 +182,9 @@
 c += 1;
   }
 }
+
+void testSwitchWithSizeofs() {
+  switch (sizeof(char) == 1) { // expected-warning{{switch condition has boolean value}}
+  case sizeof(char):; // no-crash
+  }
+}
Index: include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
@@ -211,7 +211,8 @@
   }
 
   const llvm::APSInt &getTruthValue(bool b, QualType T) {
-return getValue(b ? 1 : 0, Ctx.getIntWidth(T), true);
+return getValue(b ? 1 : 0, Ctx.getIntWidth(T),
+T->isUnsignedIntegerOrEnumerationType());
   }
 
   const llvm::APSInt &getTruthValue(bool b) {
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50382: [analyzer] Fix a typo in `RegionStore.txt`.

2018-08-07 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.

Yay somebody reads those.


Repository:
  rC Clang

https://reviews.llvm.org/D50382



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


[PATCH] D49199: [analyzer][UninitializedObjectChecker] Pointer/reference objects are dereferenced according to dynamic type

2018-08-07 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.

This looks roughly correct, but at the same time none of the tests actually 
exercise the dynamic type propagation. In these tests all the necessary 
information is obtained from the structure of the MemRegion (directly or via 
the initial `StripCasts`), not from the dynamic type map that is an additional 
layer of metadata over the program state. The actual test would assume, as an 
example, chasing undefined values through a symbolic pointer produced by 
`operator new()` - which is a symbolic void pointer, but it points to a 
well-defined type of object. Because we skip symbolic pointers for now, i guess 
you cannot really write such tests. But at the same time chasing through 
//heap// symbolic pointers (i.e., pointers in the heap //memory space//) should 
be safe (so safe that they shouldn't really have been implemented as symbolic 
pointers in the first place).


https://reviews.llvm.org/D49199



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


[PATCH] D48436: [analyzer][UninitializedObjectChecker] Fixed a false negative by no longer filtering out certain constructor calls

2018-08-07 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

Ok, let's commit this and see how to fix it later. I still think it's more 
important to come up with clear rules of who is responsible for initializing 
fields than making sure our warnings are properly grouped together.

> ideally we wouldn't like to have a warning for an object (`t`) and a separate 
> warning for it's field (`t.bptr.x`)

I don't quite understand this. How would the first warning look like? What 
would it warn about?




Comment at: lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp:669-671
+  Optional CurrentObject = getObjectVal(Ctor, 
Context);
+  if (!CurrentObject)
+return false;

All uses of `getObjectVal` so far are followed by retrieving the parent region 
from the `LazyCompoundVal`. Why do you need to obtain the `LazyCompoundVal` in 
the first place, i.e. do the second `getSVal` "for '*this'" in `getObjectVal`? 
Why not just operate over the this-region on the current Store? I think there 
isn't even a guarantee that these two regions are the same. Like, in this case 
they probably will be the same, but we shouldn't rely on that.


https://reviews.llvm.org/D48436



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


[PATCH] D50408: [analyzer] Avoid querying this-pointers for static-methods.

2018-08-07 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

Yup, fair enough.


https://reviews.llvm.org/D50408



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


[PATCH] D50487: [CFG] [analyzer] Find argument constructors in CXXTemporaryObjectExprs.

2018-08-08 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, mikhail.ramalho, baloghadamsoftware.

`CXXTemporaryObjectExpr` is a sub-class of `CXXConstructExpr` that represents a 
construct-expression written as `T(Arg1, ..., ArgN)`, where `N` is not equal to 
1. If `N` is equal to 1, such expression is parsed as a construction conversion 
from type of `Arg1` to type `T` instead. Not every temporary object is 
represented by `CXXTemporaryObjectExpr` (for instance, implicit temporary 
copies aren't), and `CXXTemporaryObjectExpr` doesn't necessarily represent a 
temporary object (constructor written as a `CXXTemporaryObjectExpr` may 
construct a pretty much arbitrary object if copy elision kicks in).

If any of `Arg1`, ..., `ArgN` requires construction, give it a construction 
context of an argument. For now it only works for `CXXConstructExpr`s that 
aren't `CXXTemporaryObjectExpr`s. I forgot to add it when i was adding stubs 
for `CXXConstructExpr` argument constructors in 
https://reviews.llvm.org/D48249. I thought i had a test for it 
(`passArgumentIntoAnotherConstructor()`), but in fact in this test `N` was 
equal to 1, so it wasn't really testing a `CXXTemporaryObjectExpr`.


Repository:
  rC Clang

https://reviews.llvm.org/D50487

Files:
  lib/Analysis/CFG.cpp
  test/Analysis/cfg-rich-constructors.cpp


Index: test/Analysis/cfg-rich-constructors.cpp
===
--- test/Analysis/cfg-rich-constructors.cpp
+++ test/Analysis/cfg-rich-constructors.cpp
@@ -820,6 +820,7 @@
 class E {
 public:
   E(D d);
+  E(D d1, D d2);
 };
 
 void useC(C c);
@@ -939,6 +940,38 @@
 void passArgumentIntoAnotherConstructor() {
   E e = E(D());
 }
+
+
+// CHECK: void passTwoArgumentsIntoAnotherConstructor()
+// CXX11-ELIDE:  1: argument_constructors::D() (CXXConstructExpr, 
[B1.2], [B1.4], [B1.5], class argument_constructors::D)
+// CXX11-NOELIDE:  1: argument_constructors::D() (CXXConstructExpr, 
[B1.2], [B1.4], class argument_constructors::D)
+// CXX11-NEXT: 2: [B1.1] (BindTemporary)
+// CXX11-NEXT: 3: [B1.2] (ImplicitCastExpr, NoOp, const class 
argument_constructors::D)
+// CXX11-NEXT: 4: [B1.3]
+// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.6], [B1.13]+0, class 
argument_constructors::D)
+// CXX11-NEXT: 6: [B1.5] (BindTemporary)
+// CXX11-ELIDE-NEXT: 7: argument_constructors::D() (CXXConstructExpr, 
[B1.8], [B1.10], [B1.11], class argument_constructors::D)
+// CXX11-NOELIDE-NEXT: 7: argument_constructors::D() (CXXConstructExpr, 
[B1.8], [B1.10], class argument_constructors::D)
+// CXX11-NEXT: 8: [B1.7] (BindTemporary)
+// CXX11-NEXT: 9: [B1.8] (ImplicitCastExpr, NoOp, const class 
argument_constructors::D)
+// CXX11-NEXT:10: [B1.9]
+// CXX11-NEXT:11: [B1.10] (CXXConstructExpr, [B1.12], [B1.13]+1, class 
argument_constructors::D)
+// CXX11-NEXT:12: [B1.11] (BindTemporary)
+// CXX11-NEXT:13: argument_constructors::E([B1.6], [B1.12]) 
(CXXConstructExpr, class argument_constructors::E)
+// CXX11-NEXT:14: ~argument_constructors::D() (Temporary object destructor)
+// CXX11-NEXT:15: ~argument_constructors::D() (Temporary object destructor)
+// CXX11-NEXT:16: ~argument_constructors::D() (Temporary object destructor)
+// CXX11-NEXT:17: ~argument_constructors::D() (Temporary object destructor)
+// CXX17:  1: argument_constructors::D() (CXXConstructExpr, [B1.2], 
[B1.5]+0, class argument_constructors::D)
+// CXX17-NEXT: 2: [B1.1] (BindTemporary)
+// CXX17-NEXT: 3: argument_constructors::D() (CXXConstructExpr, [B1.4], 
[B1.5]+1, class argument_constructors::D)
+// CXX17-NEXT: 4: [B1.3] (BindTemporary)
+// CXX17-NEXT: 5: argument_constructors::E([B1.2], [B1.4]) 
(CXXConstructExpr, class argument_constructors::E)
+// CXX17-NEXT: 6: ~argument_constructors::D() (Temporary object destructor)
+// CXX17-NEXT: 7: ~argument_constructors::D() (Temporary object destructor)
+void passTwoArgumentsIntoAnotherConstructor() {
+  E(D(), D());
+}
 } // end namespace argument_constructors
 
 namespace copy_elision_with_extra_arguments {
Index: lib/Analysis/CFG.cpp
===
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -4352,6 +4352,11 @@
 
 CFGBlock *CFGBuilder::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C,
   AddStmtChoice asc) {
+  // If the constructor takes objects as arguments by value, we need to 
properly
+  // construct these objects. Construction contexts we find here aren't for the
+  // constructor C, they're for its arguments only.
+  findConstructionContextsForArguments(C);
+
   autoCreateBlock();
   appendConstructor(Block, C);
   return VisitChildren(C);


Index: test/Analysis/cfg-rich-constructors.cpp
==

[PATCH] D50488: [Analyzer] Checker for non-determinism caused by sorting of pointer-like keys

2018-08-09 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Hmm, this might make a good `optin.*` checker. Also i see that this is a pure 
AST-based checker; did you consider putting this into `clang-tidy`? Like, you 
shouldn't if you're not using `clang-tidy` yourself (it's a bit messy) but this 
is an option. Also we're actively using `ASTMatchers` in Analyzer checkers 
anyway these days because they're just shorter and easier to read and generally 
cool.




Comment at: lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp:91-100
+  const QualType IterTy = CE->getArg(0)->getType();
+  const RecordDecl *RD =
+IterTy->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+
+  if (RD->field_empty())
+return;
+

This heuristic ("the argument of `std::sort` is //a class whose first field is 
of pointer type//") is quite wonky, do you have a plan on how to make it more 
precise?


Repository:
  rC Clang

https://reviews.llvm.org/D50488



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


[PATCH] D50488: [Analyzer] Checker for non-determinism caused by sorting of pointer-like keys

2018-08-10 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

> I'm only a beginner, but here are some things that caught my eye. I really 
> like the idea! :)

Thanks, i appreciate help with reviews greatly.




Comment at: lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp:93
+  const RecordDecl *RD =
+IterTy->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+

Szelethus wrote:
> Is there a reason for not directly acquiring the record declaration?
I suspect it's about typedefs. We usually use `getCanonicalType()` for this 
purpose.



Comment at: test/Analysis/ptr-sort.cpp:1
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.nondeterminism.PointerSorting %s -analyzer-output=text 
-verify
+

Szelethus wrote:
> Always run the core checkers too.
> `-analyzer-checker=core,alpha.nondeterminism.PointerSorting`
This is a good discipline, but it's not very useful in case of AST-based 
checkers because they don't interact at all with each other.


Repository:
  rC Clang

https://reviews.llvm.org/D50488



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


[PATCH] D49570: [analyzer] Improve warning messages and notes of InnerPointerChecker

2018-08-10 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

I think this is way easier to understand :)


https://reviews.llvm.org/D49570



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


[PATCH] D50594: [analyzer] [NFC] Introduce separate targets for testing the analyzer: check-clang-analyzer and check-clang-analyzer-z3

2018-08-13 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

Yup, i hope it'll be comfy now.


https://reviews.llvm.org/D50594



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


[PATCH] D32747: [Analyzer] Iterator Checker - Part 3: Invalidation check, first for (copy) assignments

2018-08-14 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.
Herald added a subscriber: Szelethus.

Looks great, let's land this!


https://reviews.llvm.org/D32747



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


[PATCH] D32845: [Analyzer] Iterator Checker - Part 4: Mismatched iterator checker for function parameters

2018-08-14 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.
Herald added a subscriber: Szelethus.

Looks good. I guess we may have to tone down the heuristic about "all template 
functions" if we see it fail.

@a.sidorin and @whisperity have some valid minor comments.


https://reviews.llvm.org/D32845



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


[PATCH] D50488: [Analyzer] Checker for non-determinism caused by sorting of pointer-like keys

2018-08-14 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

I guess one of the things the analyzer could find with path-sensitive analysis 
is direct comparison of non-aliasing pointers. Not only this is 
non-deterministic, but there's a related problem that comparison for equality 
would always yield false and is therefore useless. However, there will be many 
false negatives because most of the time it's hard to figure out if pointers 
alias by looking at a small part of the program (a call stack of at most 3-4 
functions in the middle of nowhere), as the analyzer does.


Repository:
  rC Clang

https://reviews.llvm.org/D50488



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


[PATCH] D32859: [Analyzer] Iterator Checker - Part 5: Move Assignment of Containers

2018-08-14 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.
Herald added a subscriber: Szelethus.

I think this patch is in good shape.

In https://reviews.llvm.org/D32859#1187551, @baloghadamsoftware wrote:

> I do not see which lines exactly you commented


This was about the `replaceSymbol()` sub. You can traverse history using the 
History tab, or if there's still a greyed out comment you can push the |<< 
button on it to jump to the respective historical diff.

In any case, i think in current form `replaceSymbol()` is much easier to 
understand, but still deserves a short comment, maybe a small example, maybe a 
better name (the word "rebase" comes to mind).


https://reviews.llvm.org/D32859



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


[PATCH] D32906: [Analyzer] Iterator Checker - Part 10: Support for iterators passed as parameter

2018-08-14 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ requested changes to this revision.
NoQ added a comment.
This revision now requires changes to proceed.
Herald added subscribers: Szelethus, mikhail.ramalho.

Let's see if this is still necessary after https://reviews.llvm.org/D49443. 
Iterators will be constructed directly into the argument region, so i suspect 
that the manual copy is no longer necessary.


https://reviews.llvm.org/D32906



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


[PATCH] D48027: [analyzer] Improve `CallDescription` to handle c++ method.

2018-08-15 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.
Herald added a subscriber: Szelethus.

This looks great, thanks, this is exactly how i imagined it!


Repository:
  rC Clang

https://reviews.llvm.org/D48027



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


[PATCH] D49722: [CStringSyntaxChecker] Check strlcat sizeof check

2018-08-15 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: cfe/trunk/test/Analysis/cstring-syntax.c:45
+  strlcpy(dest, "012345678", sizeof(dest));
+  strlcat(dest, "910", sizeof(dest)); // expected-warning {{The third argument 
allows to potentially copy more bytes than it should. Replace with the value  
  - strlen(dest) - 1 or lower}}
+  strlcpy(dest, "0123456789", sizeof(dest));

There seem to be two spaces around ``.


Repository:
  rL LLVM

https://reviews.llvm.org/D49722



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


[PATCH] D50824: [CFG] [analyzer] pr37769: Disable argument construction contexts for variadic functions.

2018-08-15 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, Szelethus, mikhail.ramalho, 
baloghadamsoftware.

The analyzer doesn't need them and they seem to have pretty weird AST from time 
to time, so let's just skip them for now.

Fixes the assertion failure in https://bugs.llvm.org/show_bug.cgi?id=38427.


Repository:
  rC Clang

https://reviews.llvm.org/D50824

Files:
  lib/Analysis/CFG.cpp
  test/Analysis/cfg-rich-constructors.cpp


Index: test/Analysis/cfg-rich-constructors.cpp
===
--- test/Analysis/cfg-rich-constructors.cpp
+++ test/Analysis/cfg-rich-constructors.cpp
@@ -1028,3 +1028,18 @@
   C(1) + C(2);
 }
 } // namespace operators
+
+namespace variadic_function_arguments {
+class C {
+ public:
+  C(int);
+};
+
+int variadic(...);
+
+// This code is quite exotic, so let's not test the CFG for it,
+// but only make sure we don't crash.
+void testCrashOnVariadicArgument() {
+  C c(variadic(0 ? c : 0)); // no-crash
+}
+} // namespace variadic_function_arguments
Index: lib/Analysis/CFG.cpp
===
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -2421,8 +2421,6 @@
 if (!boundType.isNull()) calleeType = boundType;
   }
 
-  findConstructionContextsForArguments(C);
-
   // If this is a call to a no-return function, this stops the block here.
   bool NoReturn = getFunctionExtInfo(*calleeType).getNoReturn();
 
@@ -2439,6 +2437,13 @@
   bool OmitArguments = false;
 
   if (FunctionDecl *FD = C->getDirectCallee()) {
+// TODO: Support construction contexts for variadic function arguments.
+// These are a bit problematic and not very useful because passing
+// C++ objects as C-style variadic arguments doesn't work in general
+// (see [expr.call]).
+if (!FD->isVariadic())
+  findConstructionContextsForArguments(C);
+
 if (FD->isNoReturn() || C->isBuiltinAssumeFalse(*Context))
   NoReturn = true;
 if (FD->hasAttr())


Index: test/Analysis/cfg-rich-constructors.cpp
===
--- test/Analysis/cfg-rich-constructors.cpp
+++ test/Analysis/cfg-rich-constructors.cpp
@@ -1028,3 +1028,18 @@
   C(1) + C(2);
 }
 } // namespace operators
+
+namespace variadic_function_arguments {
+class C {
+ public:
+  C(int);
+};
+
+int variadic(...);
+
+// This code is quite exotic, so let's not test the CFG for it,
+// but only make sure we don't crash.
+void testCrashOnVariadicArgument() {
+  C c(variadic(0 ? c : 0)); // no-crash
+}
+} // namespace variadic_function_arguments
Index: lib/Analysis/CFG.cpp
===
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -2421,8 +2421,6 @@
 if (!boundType.isNull()) calleeType = boundType;
   }
 
-  findConstructionContextsForArguments(C);
-
   // If this is a call to a no-return function, this stops the block here.
   bool NoReturn = getFunctionExtInfo(*calleeType).getNoReturn();
 
@@ -2439,6 +2437,13 @@
   bool OmitArguments = false;
 
   if (FunctionDecl *FD = C->getDirectCallee()) {
+// TODO: Support construction contexts for variadic function arguments.
+// These are a bit problematic and not very useful because passing
+// C++ objects as C-style variadic arguments doesn't work in general
+// (see [expr.call]).
+if (!FD->isVariadic())
+  findConstructionContextsForArguments(C);
+
 if (FD->isNoReturn() || C->isBuiltinAssumeFalse(*Context))
   NoReturn = true;
 if (FD->hasAttr())
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, Szelethus, mikhail.ramalho, 
baloghadamsoftware.

Despite the effort to eliminate the need for 
`skipRValueSubobjectAdjustments()`, we still encounter ASTs that require it 
from time to time, for example in https://bugs.llvm.org/show_bug.cgi?id=37578

One way of properly modeling such expressions would be to include the 
member-expression "adjustment" into the //construction context// of the 
temporary, but that's not something i'm planning to do, because such ASTs are 
rare and seem to be only becoming more rare over time, so for now i'm just 
fixing the old code.

The root cause of the problem in this example is that while evaluating the 
`MemberExpr` in

  `-MemberExpr 0x7fd5ef0035b8  'a::(anonymous struct at 
test.cpp:2:3)' . 0x7fd5ee06a068
`-CXXTemporaryObjectExpr 0x7fd5ef001f70  'a' 'void () 
noexcept' zeroing

there's no way for `createTemporaryRegionIfNeeded()` to communicate the newly 
created temporary region through the Environment (as it usually does), because 
all expressions so far have been prvalues.

The current code works around that problem by binding the region to the 
`CXXTemporaryObjectExpr`, which is of course a bad thing to do because we 
should not bind `Loc`s to prvalue expressions, and it leads to a crash when 
eventually this bad binding propagates to the Store and the Store is unable to 
load it.

The solution is to bind the correct [lazy compound] value to the 
`CXXTemporaryObjectExpr` and then communicate the region to the caller directly 
via an out-parameter.


Repository:
  rC Clang

https://reviews.llvm.org/D50855

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  test/Analysis/temporaries.cpp

Index: test/Analysis/temporaries.cpp
===
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -1152,3 +1152,23 @@
 // and the non-definition decl should be found by direct lookup.
 void T::foo(C) {}
 } // namespace argument_virtual_decl_lookup
+
+namespace union_indirect_field_crash {
+union U {
+  struct {
+int x;
+  };
+};
+
+template  class C {
+public:
+  void foo() const {
+(void)(true ? U().x : 0);
+  }
+};
+
+void test() {
+  C c;
+  c.foo();
+}
+} // namespace union_indirect_field_crash
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -283,11 +283,10 @@
   return state;
 }
 
-ProgramStateRef
-ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
-  const LocationContext *LC,
-  const Expr *InitWithAdjustments,
-  const Expr *Result) {
+ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
+ProgramStateRef State, const LocationContext *LC,
+const Expr *InitWithAdjustments, const Expr *Result,
+const SubRegion **OutRegionWithAdjustments) {
   // FIXME: This function is a hack that works around the quirky AST
   // we're often having with respect to C++ temporaries. If only we modelled
   // the actual execution order of statements properly in the CFG,
@@ -297,8 +296,11 @@
   if (!Result) {
 // If we don't have an explicit result expression, we're in "if needed"
 // mode. Only create a region if the current value is a NonLoc.
-if (!InitValWithAdjustments.getAs())
+if (!InitValWithAdjustments.getAs()) {
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = nullptr;
   return State;
+}
 Result = InitWithAdjustments;
   } else {
 // We need to create a region no matter what. For sanity, make sure we don't
@@ -418,11 +420,16 @@
   // The result expression would now point to the correct sub-region of the
   // newly created temporary region. Do this last in order to getSVal of Init
   // correctly in case (Result == Init).
-  State = State->BindExpr(Result, LC, Reg);
+  if (Result->isGLValue())
+State = State->BindExpr(Result, LC, Reg);
+  else
+State = State->BindExpr(Result, LC, InitValWithAdjustments);
 
   // Notify checkers once for two bindLoc()s.
   State = processRegionChange(State, TR, LC);
 
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = cast(Reg.getAsRegion());
   return State;
 }
 
@@ -2533,8 +2540,11 @@
   }
 
   // Handle regular struct fields / member variables.
-  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
-  SVal baseExprVal = state->getSVal(BaseExpr, LCtx);
+  const SubRegion *MR;
+  state =
+  createTemporaryRegionIfNeeded(state, LCtx, BaseExpr, nullptr, &MR);
+  SVal baseExprVal =
+  MR ? loc::MemRegionVal(MR) : state->

[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 161073.
NoQ added a comment.

Add comments for optional arguments.

The reason why i chose an out-parameter rather than returning a std::pair is 
because most callers //presumably// don't need this feature.


https://reviews.llvm.org/D50855

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  test/Analysis/temporaries.cpp

Index: test/Analysis/temporaries.cpp
===
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -1152,3 +1152,23 @@
 // and the non-definition decl should be found by direct lookup.
 void T::foo(C) {}
 } // namespace argument_virtual_decl_lookup
+
+namespace union_indirect_field_crash {
+union U {
+  struct {
+int x;
+  };
+};
+
+template  class C {
+public:
+  void foo() const {
+(void)(true ? U().x : 0);
+  }
+};
+
+void test() {
+  C c;
+  c.foo();
+}
+} // namespace union_indirect_field_crash
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -283,11 +283,10 @@
   return state;
 }
 
-ProgramStateRef
-ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
-  const LocationContext *LC,
-  const Expr *InitWithAdjustments,
-  const Expr *Result) {
+ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
+ProgramStateRef State, const LocationContext *LC,
+const Expr *InitWithAdjustments, const Expr *Result,
+const SubRegion **OutRegionWithAdjustments) {
   // FIXME: This function is a hack that works around the quirky AST
   // we're often having with respect to C++ temporaries. If only we modelled
   // the actual execution order of statements properly in the CFG,
@@ -297,8 +296,11 @@
   if (!Result) {
 // If we don't have an explicit result expression, we're in "if needed"
 // mode. Only create a region if the current value is a NonLoc.
-if (!InitValWithAdjustments.getAs())
+if (!InitValWithAdjustments.getAs()) {
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = nullptr;
   return State;
+}
 Result = InitWithAdjustments;
   } else {
 // We need to create a region no matter what. For sanity, make sure we don't
@@ -418,11 +420,16 @@
   // The result expression would now point to the correct sub-region of the
   // newly created temporary region. Do this last in order to getSVal of Init
   // correctly in case (Result == Init).
-  State = State->BindExpr(Result, LC, Reg);
+  if (Result->isGLValue())
+State = State->BindExpr(Result, LC, Reg);
+  else
+State = State->BindExpr(Result, LC, InitValWithAdjustments);
 
   // Notify checkers once for two bindLoc()s.
   State = processRegionChange(State, TR, LC);
 
+  if (OutRegionWithAdjustments)
+*OutRegionWithAdjustments = cast(Reg.getAsRegion());
   return State;
 }
 
@@ -2533,8 +2540,12 @@
   }
 
   // Handle regular struct fields / member variables.
-  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
-  SVal baseExprVal = state->getSVal(BaseExpr, LCtx);
+  const SubRegion *MR;
+  state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr,
+/*Result=*/nullptr,
+/*OutRegionWithAdjustments=*/&MR);
+  SVal baseExprVal =
+  MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, LCtx);
 
   const auto *field = cast(Member);
   SVal L = state->getLValue(field, baseExprVal);
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -734,10 +734,14 @@
   ///
   /// If \p Result is provided, the new region will be bound to this expression
   /// instead of \p InitWithAdjustments.
-  ProgramStateRef createTemporaryRegionIfNeeded(ProgramStateRef State,
-const LocationContext *LC,
-const Expr *InitWithAdjustments,
-const Expr *Result = nullptr);
+  ///
+  /// Returns the temporary region with adjustments into the optional
+  /// OutRegionWithAdjustments out-parameter if a new region was indeed needed,
+  /// otherwise sets it to nullptr.
+  ProgramStateRef createTemporaryRegionIfNeeded(
+  ProgramStateRef State, const LocationContext *LC,
+  const Expr *InitWithAdjustments, const Expr *Result = nullptr,
+  const SubRegion **OutRegionWithAdjustments = nullptr);
 
   /// Returns a region representing the first element

[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

> which is of course a bad thing to do because we should not bind `Loc`s to 
> `prvalue` expressions

... of non-pointer type. On the other hand, binding `NonLoc`s to glvalue 
expressions is always a bad thing to do; but, for the reference, adding this as 
an assertion currently crashes 92 tests.


https://reviews.llvm.org/D50855



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


[PATCH] D50855: [analyzer] pr37578: Fix lvalue/rvalue problem in field-of-temporary adjustments.

2018-08-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

...and adding the aforementioned assertion for prvalues increases the  number 
of crashes on tests to 196. It seems that the analyzer assigns values of 
improper loc-less very often, but then immediately overwrites them :/ I wonder 
how much performance could be gained by fixing these bugs.


https://reviews.llvm.org/D50855



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


[PATCH] D50866: [analyzer] CFRetainReleaseChecker: Avoid checking C++ methods with the same name.

2018-08-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, george.karpenkov.
Herald added subscribers: cfe-commits, Szelethus, mikhail.ramalho, a.sidorin, 
szepet, baloghadamsoftware, xazax.hun.

CFRetainReleaseChecker is a tiny checker that verifies that arguments of 
CoreFoundation retain/release functions are non-null. The checker accidentally 
checks all functions with the respective name, not just the actual `CFRetain` 
etc, which has caused a crash.

Fix it and modernize the checker to use CallEvent/CallDescription APIs to avoid 
further mistakes.

rdar://problem/42433152


Repository:
  rC Clang

https://reviews.llvm.org/D50866

Files:
  lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
  test/Analysis/retain-release.mm

Index: test/Analysis/retain-release.mm
===
--- test/Analysis/retain-release.mm
+++ test/Analysis/retain-release.mm
@@ -470,3 +470,18 @@
 void rdar33832412() {
   void* x = IOBSDNameMatching(); // no-warning
 }
+
+
+namespace member_CFRetains {
+class Foo {
+public:
+  void CFRetain(const Foo &) {}
+  void CFRetain(int) {}
+};
+
+void bar() {
+  Foo foo;
+  foo.CFRetain(foo); // no-warning
+  foo.CFRetain(0); // no-warning
+}
+}
Index: lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
===
--- lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -36,6 +36,7 @@
 
 using namespace clang;
 using namespace ento;
+using namespace llvm;
 
 namespace {
 class APIMisuse : public BugType {
@@ -531,93 +532,59 @@
 //===--===//
 
 namespace {
-class CFRetainReleaseChecker : public Checker< check::PreStmt > {
-  mutable std::unique_ptr BT;
-  mutable IdentifierInfo *Retain, *Release, *MakeCollectable, *Autorelease;
+class CFRetainReleaseChecker : public Checker {
+  mutable APIMisuse BT{this, "null passed to CF memory management function"};
+  CallDescription CFRetain{"CFRetain", 1},
+  CFRelease{"CFRelease", 1},
+  CFMakeCollectable{"CFMakeCollectable", 1},
+  CFAutorelease{"CFAutorelease", 1};
 
 public:
-  CFRetainReleaseChecker()
-  : Retain(nullptr), Release(nullptr), MakeCollectable(nullptr),
-Autorelease(nullptr) {}
-  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
 };
 } // end anonymous namespace
 
-void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
+void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
   CheckerContext &C) const {
-  // If the CallExpr doesn't have exactly 1 argument just give up checking.
-  if (CE->getNumArgs() != 1)
+  // TODO: Make this check part of CallDescription.
+  if (!Call.isGlobalCFunction())
 return;
 
-  ProgramStateRef state = C.getState();
-  const FunctionDecl *FD = C.getCalleeDecl(CE);
-  if (!FD)
-return;
-
-  if (!BT) {
-ASTContext &Ctx = C.getASTContext();
-Retain = &Ctx.Idents.get("CFRetain");
-Release = &Ctx.Idents.get("CFRelease");
-MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
-Autorelease = &Ctx.Idents.get("CFAutorelease");
-BT.reset(new APIMisuse(
-this, "null passed to CF memory management function"));
-  }
-
   // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
-  const IdentifierInfo *FuncII = FD->getIdentifier();
-  if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable ||
-FuncII == Autorelease))
+  if (!(Call.isCalled(CFRetain) || Call.isCalled(CFRelease) ||
+Call.isCalled(CFMakeCollectable) || Call.isCalled(CFAutorelease)))
 return;
 
-  // FIXME: The rest of this just checks that the argument is non-null.
-  // It should probably be refactored and combined with NonNullParamChecker.
-
   // Get the argument's value.
-  const Expr *Arg = CE->getArg(0);
-  SVal ArgVal = C.getSVal(Arg);
+  SVal ArgVal = Call.getArgSVal(0);
   Optional DefArgVal = ArgVal.getAs();
   if (!DefArgVal)
 return;
 
-  // Get a NULL value.
-  SValBuilder &svalBuilder = C.getSValBuilder();
-  DefinedSVal zero =
-  svalBuilder.makeZeroVal(Arg->getType()).castAs();
-
-  // Make an expression asserting that they're equal.
-  DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
-
-  // Are they equal?
-  ProgramStateRef stateTrue, stateFalse;
-  std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
+  // Is it null?
+  ProgramStateRef state = C.getState();
+  ProgramStateRef stateNonNull, stateNull;
+  std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal);
 
-  if (stateTrue && !stateFalse) {
-ExplodedNode *N = C.generateErrorNode(stateTrue);
+  if (!stateNonNull) {
+ExplodedNode *N = C.generateErrorNode(stateNull);
 if 

[PATCH] D32902: [Analyzer] Iterator Checker - Part 7: Support for push and pop operations

2018-08-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.
Herald added subscribers: Szelethus, mikhail.ramalho.



Comment at: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp:1477-1530
+bool isPushBackCall(const FunctionDecl *Func) {
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+return false;
+  if (Func->getNumParams() != 1)
+return false;
+  return IdInfo->getName() == "push_back";

I guess we should think if we want to use `CallDescription` for these when 
D48027 lands.


https://reviews.llvm.org/D32902



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


[PATCH] D50747: [analyzer] Drop support for GC mode in RetainCountChecker

2018-08-17 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

Yay.


Repository:
  rC Clang

https://reviews.llvm.org/D50747



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


[PATCH] D50892: [analyzer][UninitializedObjectChecker] Correct dynamic type is acquired for record pointees

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



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:187-191
   // If FR is a pointer pointing to a non-primitive type.
   if (Optional RecordV =
   DerefdV.getAs()) {
 
 const TypedValueRegion *R = RecordV->getRegion();

This looks like one more situation where we dereference a location to get a 
value and then struggle to get back to the location that we've dereferenced by 
looking at the value. Can we just use `V`?



Comment at: test/Analysis/cxx-uninitialized-object-inheritance.cpp:787
   // TODO: we'd expect the note: {{uninitialized field 'this->x'}}
   int x; // no-note
 };

Szelethus wrote:
> The checker should be able to catch this one -- for some reason it is 
> regarded as an unknown region. Odd, as the test case right after this one 
> works perfectly.
There's a variety of problems we have with empty base classes, might be one of 
those, and they are usually easy to fix because, well, yes it's a special case, 
but it's also an extremely simple case.

I encourage you to open up the Exploded Graph and study it carefully to see 
what and where goes wrong (not for this revision).


Repository:
  rC Clang

https://reviews.llvm.org/D50892



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


[PATCH] D50904: [analyzer][UninitializedObjectChecker] Added documentation to the checker list

2018-08-17 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added inline comments.
This revision is now accepted and ready to land.



Comment at: www/analyzer/alpha_checks.html:334-336
+The checker regards inherited fields as direct fields, so one
+will recieve warnings for uninitialized inherited data members
+as well. 

Szelethus wrote:
> This statement has been debated for a while, so I'm all for discussing it, as 
> I feel I've gained a lot more knowledge about this subject since it was last 
> mentioned.
The documentation should still reflect the //actual// state of things, right?


https://reviews.llvm.org/D50904



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


[PATCH] D50905: [analyzer][UninitializedObjectChecker][WIP] Explicit namespace resolution for inherited data members

2018-08-17 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added inline comments.
This revision is now accepted and ready to land.



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp:114-115
+  virtual void printNode(llvm::raw_ostream &Out) const override {
+Out << StringRef(BaseClassT.getAsString()).ltrim("struct ").ltrim("class ")
+<< "::";
+  }

Maybe just `BaseClassT->getAsCXXRecordDecl()->getName()`?


Repository:
  rC Clang

https://reviews.llvm.org/D50905



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


[PATCH] D50509: [analyzer][UninitializedObjectChecker] Refactoring p6.: Move dereferencing to a function

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



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:223
+  // int*).
+  while (auto Tmp = V.getAs()) {
+// We can't reason about symbolic regions, assume its initialized.

Hmm, i still have concerns about things like `int *x = (int *)&x;`. Why not 
just check the type to terminate the loop? Type hierarchy is guaranteed to be 
finite.


https://reviews.llvm.org/D50509



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


[PATCH] D50509: [analyzer][UninitializedObjectChecker] Refactoring p6.: Move dereferencing to a function

2018-08-17 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.

Which shouldn't prevent us from moving code around.


https://reviews.llvm.org/D50509



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


[PATCH] D50509: [analyzer][UninitializedObjectChecker] Refactoring p6.: Move dereferencing to a function

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



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:223
+  // int*).
+  while (auto Tmp = V.getAs()) {
+// We can't reason about symbolic regions, assume its initialized.

Szelethus wrote:
> NoQ wrote:
> > Hmm, i still have concerns about things like `int *x = (int *)&x;`. Why not 
> > just check the type to terminate the loop? Type hierarchy is guaranteed to 
> > be finite.
> There actually is a testcase for that -- it would create a 
> nonloc::LocAsInteger, not a loc::MemRegionVal.
> 
> I'll add a TODO to revisit this loop condition (again :) ).
Ok, let's try with one more asterisk:
```
1 void test() {
2   int **x = (int **)&x;
3   int *y = *x;
4   int z = *y;
5 }
```

Here's what i get in the Store:
```
(x,0,direct) : &element{x,0 S64b,int *}
(y,0,direct) : &element{x,0 S64b,int *}
(z,0,direct) : &element{x,0 S64b,int *}
```


https://reviews.llvm.org/D50509



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


[PATCH] D50866: [analyzer] CFRetainReleaseChecker: Avoid checking C++ methods with the same name.

2018-08-20 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp:537
+  mutable APIMisuse BT{this, "null passed to CF memory management function"};
+  CallDescription CFRetain{"CFRetain", 1},
+  CFRelease{"CFRelease", 1},

george.karpenkov wrote:
> I personally would prefer being less fancy, and avoiding the comma operator, 
> but I suppose it's a matter of style.
This isn't comma operator, just initializer list.

Alternatives are:

- `CallDescription CFRetain = {"CFRetain", 1}` (longer but looks the same)

- `CallDescription CFRetain = CallDescription("CFRetain", 1), ...` (longer and 
duplicates type)

- `CallDescription CFRetain;` `CFRetainReleaseChecker(): CFRetain("CFRetain", 
1)` (longer and duplicates variable name)




Comment at: lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp:567
+  ProgramStateRef stateNonNull, stateNull;
+  std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal);
 

george.karpenkov wrote:
> There's also DefArgVal
?


Repository:
  rC Clang

https://reviews.llvm.org/D50866



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


[PATCH] D49811: [analyzer] Obtain a ReturnStmt from a CFGAutomaticObjDtor

2018-08-21 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.
Herald added a subscriber: Szelethus.

In https://reviews.llvm.org/D49811#1175927, @NoQ wrote:

> Devin has recently pointed out that we might have as well reordered CFG 
> elements to have return statement kick in after automatic destructors, so 
> that callbacks were called in a different order. I don't see any problems 
> with that solution, but let's stick to the current solution for now, because 
> who knows.




In https://bugs.llvm.org/show_bug.cgi?id=11645#c9 Ted wrote:

> If we treat an occurrence of "return" in the CFG is meaning "bind an 
> expression result for the return value" and not as a transfer of control then 
> it is is fine for the destructors to appear after the "return".  From this 
> view, the transfer back to the caller is when we hit the Exit block.


Another possible solution is to check in the destructor if the newly dangling 
pointer is already bound to the return statement.


Repository:
  rC Clang

https://reviews.llvm.org/D49811



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


[PATCH] D51057: [analyzer][UninitializedObjectChecker] Fixed dereferencing

2018-08-21 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Yup, this looks great and that's exactly how i imagined it.




Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h:261-265
+inline bool isLocType(const QualType &T) {
+  return T->isAnyPointerType() || T->isReferenceType() ||
+ T->isBlockPointerType();
+}
+

We have a fancy static `Loc::isLocType()`.



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:126-127
   if (V.isUndef()) {
+assert(!FR->getDecl()->getType()->isReferenceType() &&
+   "References must be initialized!");
 return addFieldToUninits(

Good catch.

It might still be possible to initialize a reference with an already-undefined 
pointer if core checkers are turned off, but we don't support turning them off, 
so i guess it's fine.



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:177
 
-  if (isPrimitiveUninit(V)) {
+  const SVal &PointeeV = State->getSVal(loc::MemRegionVal(R));
+  if (isPrimitiveUninit(PointeeV)) {

Just `SVal`. And you should be able to pass `R` directly into `getSVal`.



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:210
   assert(V.getAs() && "V must be loc::MemRegionVal!");
+  auto Tmp = V.getAs();
+

We usually just do `.getAsRegion()`.

And then later `dyn_cast` it.



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:212-216
+  // We can't reason about symbolic regions, assume its initialized.
+  // Note that this also avoids a potential infinite recursion, because
+  // constructors for list-like classes are checked without being called, and
+  // the Static Analyzer will construct a symbolic region for Node *next; or
+  // similar code snippets.

Ok, i have a new concern that i didn't think about before.

There's nothing special about symbolic regions. You can make a linked list 
entirely of concrete regions:

```
struct List {
  List *next;
  List(): next(this) {}
};

void foo() {
  List list;
}
```

In this case the finite-ness of the type system won't save us.

I guess we could also memoize base regions that we visit :/ This is guaranteed 
to terminate because all of our concrete regions are based either on AST nodes 
(eg. global variables) or on certain events that happened previously during 
analysis (eg. local variables or temporaries, as they depend on the stack frame 
which must have been previously entered). I don't immediately see a better 
solution.



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:223
+  const auto *R = Tmp->getRegionAs();
+  assert(R);
+

We might have eliminated symbolic regions but we may still have concrete 
function pointers (which are `TypedRegion`s that aren't `TypedValueRegion`s, 
it's pretty weird), and i guess we might even encounter an `AllocaRegion` 
(which is completely untyped). I guess we should not try to dereference them 
either.



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:240-244
+if (Tmp->getRegion()->getSymbolicBase())
   return None;
-}
 
-V = State->getSVal(*Tmp, DynT);
+DynT = DynT->getPointeeType();
+R = Tmp->getRegionAs();

This code seems to be duplicated with the "0th iteration" before the loop. I 
guess you can put everything into the loop.


Repository:
  rC Clang

https://reviews.llvm.org/D51057



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


[PATCH] D50892: [analyzer][UninitializedObjectChecker] Correct dynamic type is acquired for record pointees

2018-08-21 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: 
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp:187-191
   // If FR is a pointer pointing to a non-primitive type.
   if (Optional RecordV =
   DerefdV.getAs()) {
 
 const TypedValueRegion *R = RecordV->getRegion();

Szelethus wrote:
> NoQ wrote:
> > This looks like one more situation where we dereference a location to get a 
> > value and then struggle to get back to the location that we've dereferenced 
> > by looking at the value. Can we just use `V`?
> I've struggled with derefencing for months now -- I'm afraid I just don't 
> really get what you'd like to see here.
> 
> Here's what I attempted to implement:
> I'd like to obtain the pointee's region of a `Loc` region, even if it has to 
> be casted to another type, like through void pointers and 
> `nonloc::LocAsInteger`, and continue analysis on said region as usual.
> 
> The trickiest part I can't seem to get right is the acquisition of the 
> pointee region. For the problem this patch attempts to solve, even though 
> `DynT` correctly says that the dynamic type is `DynTDerived2 *`, `DerefdV` 
> contains a region for `DynTBase`.
> 
> I uploaded a new patch, D51057, which hopefully settles derefence related 
> issues. Please note that it **does not **replace this diff, as the acquired 
> region is still of type `DynTBase`.
> 
> I find understanding these intricate details of the analyzer very difficult, 
> as I found very little documentation about how this works, which often left 
> me guessing what the proper way to do this is. Can you recommend some 
> literature for me on this field?
> Can you recommend some literature for me on this field?

This is pretty specific to our analyzer. `SVal`/`SymExpr`/`MemRegion` hierarchy 
is tightly coupled to implementation details of the `RegionStore`, which is our 
memory model. There's a paper on it [1]. We have some in-tree documentation of 
the `RegionStore` [2] (other docs there are also interesting to read). And 
there's my old workbook [3]. And i guess that's it.

[1] Xu, Zhongxing & Kremenek, Ted & Zhang, Jian. (2010). A Memory Model for 
Static Analysis of C Programs. 535-548. 10.1007/978-3-642-16558-0_44.
[2] 
https://github.com/llvm-mirror/clang/blob/master/docs/analyzer/RegionStore.txt
[3] 
https://github.com/haoNoQ/clang-analyzer-guide/releases/download/v0.1/clang-analyzer-guide-v0.1.pdf


Repository:
  rC Clang

https://reviews.llvm.org/D50892



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


[PATCH] D50616: [Fixed Point Arithmetic] FixedPointCast

2018-08-22 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/ExprEngineC.cpp:419
+  case CK_LValueBitCast:
+  case CK_FixedPointCast: {
 state =

a.sidorin wrote:
> Should we consider this construction as unsupported rather than supported as 
> a normal cast?
Uhm, this code seems to be held together by magic. We squeeze all sorts of 
casts (eg., float casts) into a subroutine that deals with casts of //lvalues// 
(!?) Fortunately, it dissolves into `SValBuilder::evalCast()` pretty quickly, 
so we don't really get punished for that. So it's not this patch's fault but 
our technical debt. I guess this change on its own doesn't make things worse, 
so i'm ok with it.


Repository:
  rC Clang

https://reviews.llvm.org/D50616



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


[PATCH] D48027: [analyzer] Improve `CallDescription` to handle c++ method.

2018-08-22 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

In https://reviews.llvm.org/D48027#1207721, @rnkovacs wrote:

> I guess it is highly unlikely for someone to write namespaces like that, and 
> if they do, I think they deserve to have them treated as a `std::string`.


Yup. On the other hand, if we specify our method as `{"basic_string", "c_str"}` 
and they make a `namespace basic_string { const char *c_str(); }`, we are 
pretty likely to //crash// when we try to obtain this-value for the call that 
isn't a method. This is still not a big deal because it's still highly 
unlikely, but we've seen crash reports of this sort, and the easier our spec 
is, the more likely it becomes that somebody has a different thing with the 
same name. For example, if we want to model iterators and we specify 
`{"begin"}` without specifying the base class (so that all sorts of containers 
were covered), we still want to specify that we're looking for a method call 
and not for a global function call.

So i believe that one of the important remaining problems with 
`CallDescription` is to teach it to discriminate between global functions and 
methods. We can do it in a number of ways:

1. Make a special sub-class for CallDescription depending on the sort of the 
function (too verbose),
2. Tag all items in the list as "record" vs. "namespace" (too many tags),
3. Dunno, tons of other boring and verbose approaches,
4. Change our PreCall/PostCall callbacks to callback templates that let allow 
the user subscribe to specific sub-classes of `CallEvent` similarly to how he 
can subscribe to different kinds of statements in 
PreStmt/PostStmt, and then the user doesn't need to write any 
code to check it dynamically.


Repository:
  rC Clang

https://reviews.llvm.org/D48027



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


[PATCH] D46187: [Analyzer] getRegisteredCheckers(): handle debug checkers too.

2018-05-14 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

In https://reviews.llvm.org/D46187#1089087, @lebedev.ri wrote:

> > I believe we could also benefit from a method of extracting the analyzer's 
> > `clang -cc1` run-line from clang-tidy. This would allow arbitrary debugging 
> > over a single analyzer invocation.
>
> I'm not really following, sorry.




In https://reviews.llvm.org/D46187#1091309, @alexfh wrote:

> In https://reviews.llvm.org/D46187#1088722, @NoQ wrote:
>
> > I believe we could also benefit from a method of extracting the analyzer's 
> > `clang -cc1` run-line from clang-tidy.
>
>
> It should be possible now using `clang-tidy -extra-arg=-v some/file.cpp` (or 
> `clang-check -extra-arg=-v ...`).


Nice!

So essentially @lebedev.ri could do `clang-check -analyze -extra-arg=-v 
../llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp`, then copy the printed `clang 
Invocation:` and append `-analyzer-checker debug.ViewCallGraph` to it.

Or maybe even something as horrible as

  clang-check -analyze -extra-arg=-Xclang -extra-arg=-analyzer-checker 
-extra-arg=-Xclang -extra-arg=debug.ViewCallGraph 
../llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp

And it sounds like the roughly right level of verbosity for discriminating 
between a user-facing feature and a definitely-not-user-facing feature.

I don't mind having a separate flag specifically for enabling debug checkers, 
but when it comes to actual debugging of the analyzer, debug checkers are 
usually not sufficient. Playing with other flags and `-analyzer-config` options 
is very often necessary, so if i was to debug the analyzer from clang-tidy, 
most of the time i'd have preferred to do the `-v` trick.


Repository:
  rC Clang

https://reviews.llvm.org/D46187



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


[PATCH] D44934: [analyzer] Improve the modeling of `memset()`.

2018-05-14 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Thank you! Looks good overall.




Comment at: lib/StaticAnalyzer/Checkers/CStringChecker.cpp:1013-1014
+
+  // Get the offset and the base region to figure out whether the offset of
+  // buffer is 0.
+  RegionOffset Offset = MR->getAsOffset();

Please say something here (or above) about why do we want our offset to be 0:
> We're about to model memset by producing a "default binding" in the Store. 
> Our current implementation - RegionStore - doesn't support default bindings 
> that don't cover the whole base region.



Comment at: lib/StaticAnalyzer/Checkers/CStringChecker.cpp:1037
+
+if (StateWholeReg && !StateNotWholeReg && CharVal.isZeroConstant()) {
+  // If the 'memset()' acts on the whole region of destination buffer and

I think we should use `StateNonNullChar` (that's computed below) instead of 
`CharVal.isZeroConstant()` because the Environment doesn't provide a guarantee 
that all symbols within it are collapsed to constants where applicable.



Comment at: lib/StaticAnalyzer/Checkers/CStringChecker.cpp:1055
+std::tie(StateNullChar, StateNonNullChar) =
+assumeZero(C, State, CharVal, Ctx.UnsignedCharTy);
+

I think this should use `IntTy` here. Because that's the type of the `memset`'s 
argument, and that's what `assumeZero()` expects.



Comment at: test/Analysis/string.c:1412
+  clang_analyzer_eval(strlen(str) >= 10); // expected-warning{{TRUE}}
+  // FIXME: This shoule be TRUE.
+  clang_analyzer_eval(str[1] == '0'); // expected-warning{{UNKNOWN}}

Typo: `should` :)


Repository:
  rC Clang

https://reviews.llvm.org/D44934



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


[PATCH] D45177: CStringChecker, check strlcpy/strlcat

2018-05-14 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

Thanks! I'll commit again.


https://reviews.llvm.org/D45177



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


[PATCH] D46823: [analyzer] const init: handle non-explicit cases more accurately

2018-05-14 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Yay thanks!

I think some cornercases would need to be dealt with.




Comment at: lib/StaticAnalyzer/Core/RegionStore.cpp:1650
+
+// If there is a list, but no init, it must be zero.
+if (i >= InitList->getNumInits())

NoQ wrote:
> Would this work correctly if the element is not of an integral or enumeration 
> type? I think this needs an explicit check.
What if we have an out-of-bounds access to a variable-length array? I don't 
think it'd yield zero.



Comment at: lib/StaticAnalyzer/Core/RegionStore.cpp:1650-1652
+// If there is a list, but no init, it must be zero.
+if (i >= InitList->getNumInits())
+  return svalBuilder.makeZeroVal(R->getElementType());

Would this work correctly if the element is not of an integral or enumeration 
type? I think this needs an explicit check.



Comment at: lib/StaticAnalyzer/Core/RegionStore.cpp:1733
+  } else {
+return svalBuilder.makeZeroVal(Ty);
+  }

Same: would this work correctly if the field is not of an integral or 
enumeration type?



Comment at: test/Analysis/initialization.c:3
+
+void clang_analyzer_dump(int);
 

We try to avoid using `dump()` on tests because it makes tests test the dump 
syntax, which isn't the point.

For checking constants, it's easier to do something like 
`clang_analyzer_eval(parr[i] == 2); // expected-warning{{TRUE}}`.

For finding undefined values, you can enable `core.uninitialized` checkers and 
receive warnings when the argument of `clang_analyzer_eval` is an uninitialized 
value. Or just increment the value.


Repository:
  rC Clang

https://reviews.llvm.org/D46823



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


[PATCH] D46902: [analyzer] Make plist-html multi-file.

2018-05-15 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, baloghadamsoftware.

Plist diagnostics support multi-file reports since forever. HTML diagnostics 
support multi-file reports since 
https://reviews.llvm.org/D30406/https://reviews.llvm.org/rC309968.

plist-html is the diagnostic output mode that produces both html and plist 
files. It's mostly useful for testing the analyzer itself because plist output 
is helpful for comparing results produced by different versions of the analyzer 
via the `utils/analyzer/CmpRuns.py` and html output allows you to immediately 
have a look at the changed reports.

Previously plist-html output produced multi-file HTML reports but only 
single-file Plist reports.

Change plist-html output to produce multi-file Plist reports as well.

I don't think we should care about backwards compatibility here because not 
only the old mode made no sense but also it doesn't make sense to use 
plist-html in any user-facing UI anyway.


Repository:
  rC Clang

https://reviews.llvm.org/D46902

Files:
  lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
  test/Analysis/diagnostics/plist-multi-file.c
  test/Analysis/diagnostics/plist-multi-file.h

Index: test/Analysis/diagnostics/plist-multi-file.h
===
--- /dev/null
+++ test/Analysis/diagnostics/plist-multi-file.h
@@ -0,0 +1,3 @@
+void foo(int *ptr) {
+  *ptr = 1; // expected-warning{{Dereference of null pointer (loaded from variable 'ptr')}}
+}
Index: test/Analysis/diagnostics/plist-multi-file.c
===
--- /dev/null
+++ test/Analysis/diagnostics/plist-multi-file.c
@@ -0,0 +1,205 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-output=plist-html -o %t.plist -verify %s
+// RUN: FileCheck --input-file=%t.plist %s
+
+#include "plist-multi-file.h"
+
+void bar() {
+  foo(0);
+}
+
+// CHECK: diagnostics
+// CHECK-NEXT: 
+// CHECK-NEXT:  
+// CHECK-NEXT:   path
+// CHECK-NEXT:   
+// CHECK-NEXT:
+// CHECK-NEXT: kindevent
+// CHECK-NEXT: location
+// CHECK-NEXT: 
+// CHECK-NEXT:  line7
+// CHECK-NEXT:  col7
+// CHECK-NEXT:  file0
+// CHECK-NEXT: 
+// CHECK-NEXT: ranges
+// CHECK-NEXT: 
+// CHECK-NEXT:   
+// CHECK-NEXT:
+// CHECK-NEXT: line7
+// CHECK-NEXT: col7
+// CHECK-NEXT: file0
+// CHECK-NEXT:
+// CHECK-NEXT:
+// CHECK-NEXT: line7
+// CHECK-NEXT: col7
+// CHECK-NEXT: file0
+// CHECK-NEXT:
+// CHECK-NEXT:   
+// CHECK-NEXT: 
+// CHECK-NEXT: depth0
+// CHECK-NEXT: extended_message
+// CHECK-NEXT: Passing null pointer value via 1st parameter 'ptr'
+// CHECK-NEXT: message
+// CHECK-NEXT: Passing null pointer value via 1st parameter 'ptr'
+// CHECK-NEXT:
+// CHECK-NEXT:
+// CHECK-NEXT: kindevent
+// CHECK-NEXT: location
+// CHECK-NEXT: 
+// CHECK-NEXT:  line7
+// CHECK-NEXT:  col3
+// CHECK-NEXT:  file0
+// CHECK-NEXT: 
+// CHECK-NEXT: ranges
+// CHECK-NEXT: 
+// CHECK-NEXT:   
+// CHECK-NEXT:
+// CHECK-NEXT: line7
+// CHECK-NEXT: col3
+// CHECK-NEXT: file0
+// CHECK-NEXT:
+// CHECK-NEXT:
+// CHECK-NEXT: line7
+// CHECK-NEXT: col8
+// CHECK-NEXT: file0
+// CHECK-NEXT:
+// CHECK-NEXT:   
+// CHECK-NEXT: 
+// CHECK-NEXT: depth0
+// CHECK-NEXT: extended_message
+// CHECK-NEXT: Calling 'foo'
+// CHECK-NEXT: message
+// CHECK-NEXT: Calling 'foo'
+// CHECK-NEXT:
+// CHECK-NEXT:
+// CHECK-NEXT: kindevent
+// CHECK-NEXT: location
+// CHECK-NEXT: 
+// CHECK-NEXT:  line1
+// CHECK-NEXT:  col1
+// CHECK-NEXT:  file1
+// CHECK-NEXT: 
+// CHECK-NEXT: depth1
+// CHECK-NEXT: extended_message
+// CHECK-NEXT: Entered call from 'bar'
+// CHECK-NEXT: message
+// CHECK-NEXT: Entered call from 'bar'
+// CHECK-NEXT:
+// CHECK-NEXT:
+// CHECK-NEXT: kindcontrol
+// CHECK-NEXT: edges
+// CHECK-NEXT:  
+// CHECK-NEXT:   
+// CHECK-NEXT:start
+// CHECK-NEXT: 
+// CHECK-NEXT:  
+// CHECK-NEXT:   line1
+// CHECK-NEXT:   col1
+// CHECK-NEXT:   file1
+// CHECK-NEXT:  
+// CHECK-NEXT:  
+// CHECK-NEXT:   line1
+// CHECK-NEXT:   col4
+// CHECK-NEXT:   file1
+// CHECK-NEXT:  
+// CHECK-NEXT: 
+// CHECK-NEXT:end
+// CHECK-NEXT: 
+// CHECK-NEXT:  
+// CHECK-NEXT:   line2
+// CHECK-NEXT:   col3
+// CHECK-NEXT:   file1
+// CHECK-NEXT:  
+// CHECK-NEXT:  
+// CHECK-NEXT:   line2
+// CHECK-NEXT:   col3
+// CHECK-NEXT:   file1
+// CHECK-NEXT:  
+// CHECK-NEXT: 
+// C

[PATCH] D46902: [analyzer] Make plist-html multi-file.

2018-05-15 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: test/Analysis/diagnostics/plist-multi-file.c:202
+// CHECK-NEXT:  
+// CHECK-NEXT:   report-{{([0-9a-f]{6})}}.html
+// CHECK-NEXT:  

Yay we have regular expressions.
`()` are needed so that not to confuse `}``}}` with `}}``}`.


Repository:
  rC Clang

https://reviews.llvm.org/D46902



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


[PATCH] D44934: [analyzer] Improve the modeling of `memset()`.

2018-05-15 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

Thanks! This looks great now.




Comment at: lib/StaticAnalyzer/Checkers/CStringChecker.cpp:1055
+std::tie(StateNullChar, StateNonNullChar) =
+assumeZero(C, State, CharVal, Ctx.UnsignedCharTy);
+

MTC wrote:
> NoQ wrote:
> > I think this should use `IntTy` here. Because that's the type of the 
> > `memset`'s argument, and that's what `assumeZero()` expects.
> I confirmed again that `memset()` will convert the value to `unsigned char` 
> first, see http://en.cppreference.com/w/c/string/byte/memset. 
> 
> In the next update, I will `evalCast(value, UnsignedCharTy, IntTy)` first, 
> therefore, I will continue to use `UnsignedCharTy`.
Aha, yup, it does convert to `unsigned char`, but `assumeZero()` doesn't. The 
new code looks correct.


Repository:
  rC Clang

https://reviews.llvm.org/D44934



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


[PATCH] D46891: [StaticAnalyzer] Added a getLValue method to ProgramState for bases

2018-05-15 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

Looks good, thanks! Yeah, seems that these were accidentally omitted.

You may want to add the `const CXXRecordDecl *` variant as well.


Repository:
  rC Clang

https://reviews.llvm.org/D46891



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


[PATCH] D46891: [StaticAnalyzer] Added a getLValue method to ProgramState for bases

2018-05-15 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h:732
+   const SubRegion *Super) const {
+  const auto Base = BaseSpec.getType()->getAsCXXRecordDecl();
+  return loc::MemRegionVal(

I think you wanted to say `const auto *` here. It's not really important that 
the local variable is const, but that the pointer points to const.


Repository:
  rC Clang

https://reviews.llvm.org/D46891



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


[PATCH] D45177: CStringChecker, check strlcpy/strlcat

2018-05-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Checkers/CStringChecker.cpp:1560-1566
 // If the size is known to be zero, we're done.
 if (StateZeroSize && !StateNonZeroSize) {
   StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal);
   C.addTransition(StateZeroSize);
   return;
 }
 

One more cornercase where the return value needs to be corrected. It'd be great 
to de-duplicate this code to avoid similar problems in the future.

Test case:
```
int foo(char *dst, const char *src) {
  return strlcpy(dst, src, 0); // no-crash
}
```


Repository:
  rC Clang

https://reviews.llvm.org/D45177



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


[PATCH] D47044: Ensure that we only visit a destructor for a reference if type information is available.

2018-05-17 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Mmm, i think loop widening simply shouldn't invalidate references (though it 
should invalidate objects bound to them). Simply because you can't really 
reassign a reference.

Could we mark them as "preserve contents", like in 
https://reviews.llvm.org/D45491?


Repository:
  rC Clang

https://reviews.llvm.org/D47044



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


[PATCH] D47007: [Sanitizer] CStringChecker fix for strlcpy when no bytes are copied to the dest buffer

2018-05-18 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

I've been thinking if we could de-duplicate this whole set of branches that 
computes the return value so that we didn't have to fix every bug twice. Maybe 
move it to an auxiliary function.




Comment at: lib/StaticAnalyzer/Checkers/CStringChecker.cpp:1795
 // copied element, or a pointer to the start of the destination buffer.
 Result = (returnEnd ? UnknownVal() : DstVal);
   } else {

Do we need to consider `returnEnd` on the short path as well?



Comment at: lib/StaticAnalyzer/Checkers/CStringChecker.cpp:1869-1875
   if (returnPtr) {
 // If this is a stpcpy-style copy, but we were unable to check for a buffer
 // overflow, we still need a result. Conjure a return value.
 if (returnEnd && Result.isUnknown()) {
   Result = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
 }
   }

Do we need to do that on the short path as well?


Repository:
  rC Clang

https://reviews.llvm.org/D47007



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


[PATCH] D47044: Ensure that we only visit a destructor for a reference if type information is available.

2018-05-18 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Hopefully.


Repository:
  rC Clang

https://reviews.llvm.org/D47044



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


[PATCH] D47044: [analyzer] Ensure that we only visit a destructor for a reference if type information is available.

2018-05-18 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Hmm, well, i guess it's not going to be a one-liner. You might have to iterate 
through all live variables in the scope, see if they are references, and add 
the trait. I think currently there's no way around scanning the current 
function body (i.e. `LCtx->getDecl()`, where `LCtx` is 
`Pred->getLocationContext()`) an AST visitor or an AST matcher. Once that's 
done, you can take `Pred->getState()->getLValue(VD, LCtx)` for every `const 
VarDecl *` `VD` you find and set the invalidation trait on that. It might be 
necessary to restrict your search to active variables (in the sense of 
`LCtx->getAnalysis()->isLive(S, VD)`), where `S` is... 
dunno, some statement that makes sense here.

Probably global/static references should also not be invalidated. It'd take 
even more effort to find those.

I still think it's worth it because widening is indeed at fault, not the common 
destructor handling code. The assertion you step upon (that the `cast<>` always 
succeeds) is a valuable assertion that helped us find that bug, we shouldn't 
suppress it.

Loop widening in its current form is an experiment that was never announced to 
work, and, well, yeah, it has this sort of bugs. There is work started by 
@szepet in https://reviews.llvm.org/D36690 to turn widening into a 
whitelist-like thing.


Repository:
  rC Clang

https://reviews.llvm.org/D47044



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


[PATCH] D44238: [CFG] Fix automatic destructors when a member is bound to a reference.

2018-05-18 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 147635.
NoQ added a comment.

Switch to `skipRValueSubobjectAdjustments`. Yup, that's much better.

Add FIXME tests for lifetime extension through non-references that are still 
not working - that correspond to a nearby FIXME in the code. I'm not planning 
to fix them immediately, but it's nice to know what else isn't working in this 
realm.


https://reviews.llvm.org/D44238

Files:
  lib/Analysis/CFG.cpp
  test/Analysis/auto-obj-dtors-cfg-output.cpp

Index: test/Analysis/auto-obj-dtors-cfg-output.cpp
===
--- test/Analysis/auto-obj-dtors-cfg-output.cpp
+++ test/Analysis/auto-obj-dtors-cfg-output.cpp
@@ -1,7 +1,11 @@
-// RUN: %clang_analyze_cc1 -fcxx-exceptions -fexceptions -analyzer-checker=debug.DumpCFG -analyzer-config cfg-rich-constructors=false %s > %t 2>&1
-// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,WARNINGS %s
-// RUN: %clang_analyze_cc1 -fcxx-exceptions -fexceptions -analyzer-checker=debug.DumpCFG -analyzer-config cfg-rich-constructors=true %s > %t 2>&1
-// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,ANALYZER %s
+// RUN: %clang_analyze_cc1 -std=c++98 -fcxx-exceptions -fexceptions -analyzer-checker=debug.DumpCFG -analyzer-config cfg-rich-constructors=false %s > %t 2>&1
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX98,WARNINGS,CXX98-WARNINGS %s
+// RUN: %clang_analyze_cc1 -std=c++98 -fcxx-exceptions -fexceptions -analyzer-checker=debug.DumpCFG -analyzer-config cfg-rich-constructors=true %s > %t 2>&1
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX98,ANALYZER,CXX98-ANALYZER %s
+// RUN: %clang_analyze_cc1 -std=c++11 -fcxx-exceptions -fexceptions -analyzer-checker=debug.DumpCFG -analyzer-config cfg-rich-constructors=false %s > %t 2>&1
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,WARNINGS,CXX11-WARNINGS %s
+// RUN: %clang_analyze_cc1 -std=c++11 -fcxx-exceptions -fexceptions -analyzer-checker=debug.DumpCFG -analyzer-config cfg-rich-constructors=true %s > %t 2>&1
+// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,ANALYZER,CXX11-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
@@ -14,6 +18,8 @@
 
 class A {
 public:
+  int x;
+
 // CHECK:  [B1 (ENTRY)]
 // CHECK-NEXT:   Succs (1): B0
 // CHECK:  [B0 (EXIT)]
@@ -70,6 +76,287 @@
 // CHECK:  [B2 (ENTRY)]
 // CHECK-NEXT:   Succs (1): B1
 // CHECK:  [B1]
+// WARNINGS-NEXT:   1: A() (CXXConstructExpr, class A)
+// CXX98-ANALYZER-NEXT:   1: A() (CXXConstructExpr, [B1.2], class A)
+// CXX11-ANALYZER-NEXT:   1: A() (CXXConstructExpr, [B1.2], [B1.3], class A)
+// CHECK-NEXT:   2: [B1.1] (BindTemporary)
+// CXX98-NEXT:   3: [B1.2].x
+// CXX98-NEXT:   4: [B1.3]
+// CXX98-NEXT:   5: const int &x = A().x;
+// CXX98-NEXT:   6: [B1.5].~A() (Implicit destructor)
+// CXX11-NEXT:   3: [B1.2]
+// CXX11-NEXT:   4: [B1.3].x
+// CXX11-NEXT:   5: [B1.4] (ImplicitCastExpr, NoOp, const int)
+// CXX11-NEXT:   6: const int &x = A().x;
+// CXX11-NEXT:   7: [B1.6].~A() (Implicit destructor)
+// CHECK-NEXT:   Preds (1): B2
+// CHECK-NEXT:   Succs (1): B0
+// CHECK:  [B0 (EXIT)]
+// CHECK-NEXT:   Preds (1): B1
+void test_const_ref_to_field() {
+  const int &x = A().x;
+}
+
+// CHECK:[B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK:[B1]
+// WARNINGS-NEXT: 1: A() (CXXConstructExpr, class A)
+// CXX98-ANALYZER-NEXT: 1: A() (CXXConstructExpr, [B1.2], class A)
+// CXX11-ANALYZER-NEXT: 1: A() (CXXConstructExpr, [B1.2], [B1.3], class A)
+// CHECK-NEXT: 2: [B1.1] (BindTemporary)
+// CXX98-NEXT: 3: A::x
+// CXX98-NEXT: 4: &[B1.3]
+// CXX98-NEXT: 5: [B1.2] .* [B1.4]
+// CXX98-NEXT: 6: [B1.5]
+// CXX98-NEXT: 7: const int &x = A() .* &A::x;
+// CXX98-NEXT: 8: [B1.7].~A() (Implicit destructor)
+// CXX11-NEXT: 3: [B1.2]
+// CXX11-NEXT: 4: A::x
+// CXX11-NEXT: 5: &[B1.4]
+// CXX11-NEXT: 6: [B1.3] .* [B1.5]
+// CXX11-NEXT: 7: [B1.6] (ImplicitCastExpr, NoOp, const int)
+// CXX11-NEXT: 8: const int &x = A() .* &A::x;
+// CXX11-NEXT: 9: [B1.8].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK:[B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+void test_pointer_to_member() {
+  const int &x = A().*&A::x;
+}
+
+// FIXME: There should be automatic destructors at the end of scope.
+// CHECK:[B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK:[B1]
+// WARNINGS-NEXT: 1: A() (CXXConstructExpr, class A)
+// ANALYZER-NEXT: 1: A() (CXXConstructExpr, [B1.2], [B1.4], class A)
+// CHECK-NEXT: 2: [B1.1] (BindTemporary)
+// CHECK-NEXT: 3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 4: [B1.3]
+// CHECK-NEXT: 5: {[B1.4]}
+// CHECK-NEXT: 6: B b = {A()};
+// WARNINGS-NEXT: 7: A() (CXXConstructExpr,

[PATCH] D47007: [analyzer] CStringChecker fix for strlcpy when no bytes are copied to the dest buffer

2018-05-18 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

@devnexen I think you should request commit access. You're already an active 
contributor :)


Repository:
  rC Clang

https://reviews.llvm.org/D47007



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


[PATCH] D44239: [analyzer] Re-enable constructor inlining when lifetime extension through fields occurs.

2018-05-18 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 147637.
NoQ added a comment.
Herald added a subscriber: baloghadamsoftware.

Rebase.


https://reviews.llvm.org/D44239

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  test/Analysis/lifetime-extension.cpp

Index: test/Analysis/lifetime-extension.cpp
===
--- test/Analysis/lifetime-extension.cpp
+++ test/Analysis/lifetime-extension.cpp
@@ -46,10 +46,18 @@
   const int &y = A().j[1]; // no-crash
   const int &z = (A().j[1], A().j[0]); // no-crash
 
-  // FIXME: All of these should be TRUE, but constructors aren't inlined.
-  clang_analyzer_eval(x == 1); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(y == 3); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(z == 2); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(x == 1);
+  clang_analyzer_eval(y == 3);
+  clang_analyzer_eval(z == 2);
+#ifdef TEMPORARIES
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+#else
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+#endif
 }
 } // end namespace pr19539_crash_on_destroying_an_integer
 
@@ -144,8 +152,12 @@
   {
 const bool &x = C(true, &after, &before).x; // no-crash
   }
-  // FIXME: Should be TRUE. Should not warn about garbage value.
-  clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(after == before);
+#ifdef TEMPORARIES
+  // expected-warning@-2{{TRUE}}
+#else
+  // expected-warning@-4{{UNKNOWN}}
+#endif
 }
 
 struct A { // A is an aggregate.
Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -695,12 +695,7 @@
   if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion)
 return CIP_DisallowedOnce;
 
-  // If the temporary is lifetime-extended by binding a smaller object
-  // within it to a reference, automatic destructors don't work properly.
-  if (CallOpts.IsTemporaryLifetimeExtendedViaSubobject)
-return CIP_DisallowedOnce;
-
-  // If the temporary is lifetime-extended by binding it to a reference-typ
+  // If the temporary is lifetime-extended by binding it to a reference-type
   // field within an aggregate, automatic destructors don't work properly.
   if (CallOpts.IsTemporaryLifetimeExtendedViaAggregate)
 return CIP_DisallowedOnce;
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -182,22 +182,13 @@
   const auto *TOCC = cast(CC);
   if (const auto *MTE = TOCC->getMaterializedTemporaryExpr()) {
 if (const ValueDecl *VD = MTE->getExtendingDecl()) {
-  // Pattern-match various forms of lifetime extension that aren't
-  // currently supported by the CFG.
-  // FIXME: Is there a better way to retrieve this information from
-  // the MaterializeTemporaryExpr?
   assert(MTE->getStorageDuration() != SD_FullExpression);
-  if (VD->getType()->isReferenceType()) {
-assert(VD->getType()->isReferenceType());
-if (VD->getType()->getPointeeType().getCanonicalType() !=
-MTE->GetTemporaryExpr()->getType().getCanonicalType()) {
-  // We're lifetime-extended via our field. Automatic destructors
-  // aren't quite working in this case.
-  CallOpts.IsTemporaryLifetimeExtendedViaSubobject = true;
-}
-  } else {
+  if (!VD->getType()->isReferenceType()) {
 // We're lifetime-extended by a surrounding aggregate.
-// Automatic destructors aren't quite working in this case.
+// Automatic destructors aren't quite working in this case
+// on the CFG side. We should warn the caller about that.
+// FIXME: Is there a better way to retrieve this information from
+// the MaterializeTemporaryExpr?
 CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true;
   }
 }
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -106,11 +106,6 @@
 bool IsTemporaryCtorOrDtor = false;
 
 /// This call is a constructor for a temporary that is lifetime-extended
-/// by binding a smaller object within it to a reference, for example
-/// 'const int &x = C().x;'.
-bool Is

[PATCH] D47135: [analyzer][WIP] A checker for dangling string pointers in C++

2018-05-21 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

This looks great, i think we should make a single super simple mock test and 
commit this.

@MTC, i really appreciate your help!




Comment at: lib/StaticAnalyzer/Checkers/DanglingStringPointerChecker.cpp:59
+  QualType RegType = TypedR->getValueType();
+  if (RegType.getAsString() != "std::string")
+return;

MTC wrote:
> A little tip, there are other string types besides `std::string`, like 
> `std::wstring`, which can be added in the future. See [[ 
> http://en.cppreference.com/w/cpp/string/basic_string | basic_string ]].
Yup, the current check should work in many cases, but it should be better to 
compare the class name to `basic_string`.

I'm also worried about various sorts of `std::__1::string`s (an inline 
namespace in libc++).



Comment at: lib/StaticAnalyzer/Checkers/DanglingStringPointerChecker.cpp:64
+
+  if (Call.isCalled(CStrFn)) {
+SymbolRef RawPtr = Call.getReturnValue().getAsSymbol();

It has long been planned to extend `isCalled` to C++ methods, i.e. something 
like this:
```
DanglingStringPointerChecker() : CStrFn("std::string::c_str") {}

...

void DanglingStringPointerChecker::checkPostCall(const CallEvent &Call,
 CheckerContext &C) const {
  // Skip the class check.
  if (Call.isCalled(CStrFn)) {
...
  }

  ...
}
```
Still looking for volunteers^^



Comment at: lib/StaticAnalyzer/Checkers/DanglingStringPointerChecker.cpp:71
+
+  if (dyn_cast(ICall)) {
+if (State->contains(TypedR)) {

MTC wrote:
> `CXXDestructorCall::classof(const CallEvent *CA)` can also be used here. 
`isa`.



Comment at: lib/StaticAnalyzer/Checkers/RegionState.h:18
+
+namespace region_state {
+

MTC wrote:
> I'm not sure whether `region_state` follows the naming conventions of LLVM 
> coding standards. Can someone answer this question?
I think it does, cf. `namespace ast_matchers`.

The name should be more specific though, a lot of checkers track "region 
states". Maybe "allocation_state"?


Repository:
  rC Clang

https://reviews.llvm.org/D47135



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


[PATCH] D47155: [analyzer] Reduce simplifySVal complexity threshold further.

2018-05-21 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, baloghadamsoftware.

Reported by Mikael Holmén on 
http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20180416/225349.html - 
a combination of https://reviews.llvm.org/rC329780 and 
https://reviews.llvm.org/rC300178 caused a performance regression that seemed 
to be a hang on the attached test case.

Reducing even further from 20 to 10 gives a ~15% further speed boost on the 
test, but i don't think it's worth it because such code is not common and 
therefore accuracy is more important.


Repository:
  rC Clang

https://reviews.llvm.org/D47155

Files:
  lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
  test/Analysis/hangs.c


Index: test/Analysis/hangs.c
===
--- /dev/null
+++ test/Analysis/hangs.c
@@ -0,0 +1,16 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker core -verify %s
+
+// expected-no-diagnostics
+
+// Stuff that used to hang.
+
+int g();
+
+int f(int y) {
+  return y + g();
+}
+
+int produce_a_very_large_symbol(int x) {
+  return f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(
+ f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(x;
+}
Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
===
--- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -1274,7 +1274,7 @@
 SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) {
   // Simplification is much more costly than computing complexity.
   // For high complexity, it may be not worth it.
-  if (V.getSymbol()->computeComplexity() > 100)
+  if (V.getSymbol()->computeComplexity() > 20)
 return V;
   return Visit(V.getSymbol());
 }


Index: test/Analysis/hangs.c
===
--- /dev/null
+++ test/Analysis/hangs.c
@@ -0,0 +1,16 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker core -verify %s
+
+// expected-no-diagnostics
+
+// Stuff that used to hang.
+
+int g();
+
+int f(int y) {
+  return y + g();
+}
+
+int produce_a_very_large_symbol(int x) {
+  return f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(
+ f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(x;
+}
Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
===
--- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -1274,7 +1274,7 @@
 SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) {
   // Simplification is much more costly than computing complexity.
   // For high complexity, it may be not worth it.
-  if (V.getSymbol()->computeComplexity() > 100)
+  if (V.getSymbol()->computeComplexity() > 20)
 return V;
   return Visit(V.getSymbol());
 }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D47155: [analyzer] Reduce simplifySVal complexity threshold further.

2018-05-21 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a reviewer: mikhail.ramalho.
NoQ added a subscriber: mikhail.ramalho.
NoQ added a comment.

@mikhail.ramalho Does it solve your problems with ffmpeg as well? :)


Repository:
  rC Clang

https://reviews.llvm.org/D47155



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


[PATCH] D47135: [analyzer][WIP] A checker for dangling string pointers in C++

2018-05-22 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

We'll have to track `string_view` ourselves, not relying on `MallocChecker`. So 
we only need an `AF_` for the pointer case.

`DanglingInternalBufferChecker` and `AF_InternalBuffer` sound great to me.


Repository:
  rC Clang

https://reviews.llvm.org/D47135



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


[PATCH] D47303: [analyzer] NFC: Merge object construction related state traits into one.

2018-05-23 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, baloghadamsoftware.

As noticed in http://lists.llvm.org/pipermail/cfe-dev/2018-May/058055.html we 
need a path-sensitive program state trait that for, essentially, every 
constructed object maintains the region of that object until the statement that 
consumes the object is encountered.

We already have such trait for new-expressions and bind/materialize temporary 
expressions, which are three separate traits. Because we plan to add more, it 
doesn't make sense to maintain that many traits that do the same thing in 
different circumstances, so i guess it's better to merge them into a single 
multi-purpose trait. "Multi-purpose" is definitely not the top buzzword in 
programming, but here i believe it's worth it because the underlying reasoning 
for needing these traits and needing them to work in a particular manner is the 
same. We need them because our constructor expressions are turned inside out, 
and we need a better connection between "inside" and "out" in both directions. 
One of these directions is handled by the ConstructionContext; the other is 
path-sensitive.

No functional change intended here; this is a refactoring.


Repository:
  rC Clang

https://reviews.llvm.org/D47303

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -288,8 +288,8 @@
   AllocV, CNE->getType(),
   getContext().getPointerType(getContext().VoidTy));
 
-  state =
-  setCXXNewAllocatorValue(state, CNE, calleeCtx->getParent(), AllocV);
+  state = addObjectUnderConstruction(state, CNE, calleeCtx->getParent(),
+ AllocV);
 }
   }
 
@@ -354,8 +354,9 @@
  /*WasInlined=*/true);
   for (auto I : DstPostPostCallCallback) {
 getCheckerManager().runCheckersForNewAllocator(
-CNE, getCXXNewAllocatorValue(I->getState(), CNE,
- calleeCtx->getParent()),
+CNE,
+*getObjectUnderConstruction(I->getState(), CNE,
+calleeCtx->getParent()),
 DstPostCall, I, *this,
 /*WasInlined=*/true);
   }
@@ -588,8 +589,8 @@
 // Conjure a temporary if the function returns an object by value.
 MemRegionManager &MRMgr = svalBuilder.getRegionManager();
 const CXXTempObjectRegion *TR = MRMgr.getCXXTempObjectRegion(E, LCtx);
-State = addAllNecessaryTemporaryInfo(State, RTC->getConstructionContext(),
- LCtx, TR);
+State = markStatementsCorrespondingToConstructedObject(
+State, RTC->getConstructionContext(), LCtx, loc::MemRegionVal(TR));
 
 // Invalidate the region so that it didn't look uninitialized. Don't notify
 // the checkers.
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -111,11 +111,10 @@
 }
 
 
-const MemRegion *
-ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
-  ExplodedNode *Pred,
-  const ConstructionContext *CC,
-  EvalCallOptions &CallOpts) {
+SVal ExprEngine::getLocationForConstructedObject(const CXXConstructExpr *CE,
+ ExplodedNode *Pred,
+ const ConstructionContext *CC,
+ EvalCallOptions &CallOpts) {
   const LocationContext *LCtx = Pred->getLocationContext();
   ProgramStateRef State = Pred->getState();
   MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
@@ -130,9 +129,8 @@
   const auto *Var = cast(DS->getSingleDecl());
   SVal LValue = State->getLValue(Var, LCtx);
   QualType Ty = Var->getType();
-  LValue =
-  makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
-  return LValue.getAsRegion();
+  return makeZeroElementRegion(State, LValue, Ty,
+   CallOpts.IsArrayCtorOrDtor);
 }
 case ConstructionContext::SimpleConstructorInitializerKind: {
   const auto *ICC = cast(CC);
@@ -156,25 +154,26 @@
   QualType Ty = Field->getType();
   FieldVal = makeZeroElementRegion(State, FieldVal, Ty,
  

[PATCH] D47304: [analyzer] NFC: Merge the functions for obtaining constructed object location and storing this location for later use.

2018-05-23 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, baloghadamsoftware.

`getLocationForConstructedObject()` looks at the construction context and 
figures out what region should represent the object.

`markStatementsCorrespondingToConstructedObject()` looks at the construction 
context and figures out what statements will need to retrieve that region 
directly later.

These functions are coupled and code is duplicated between them. They should be 
merged. The resulting function is large, so it'd probably later need to be 
split in a different manner (i.e. by construction context kinds). It'll also 
soon become recursive as we add better support for copy elision at return 
sites. I really hope we don't end up coding any sort of 
ConstructionContextVisitor.

No functional change intended here; this is a refactoring pass.


Repository:
  rC Clang

https://reviews.llvm.org/D47304

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -587,18 +587,20 @@
   unsigned Count = currBldrCtx->blockCount();
   if (auto RTC = getCurrentCFGElement().getAs()) {
 // Conjure a temporary if the function returns an object by value.
-MemRegionManager &MRMgr = svalBuilder.getRegionManager();
-const CXXTempObjectRegion *TR = MRMgr.getCXXTempObjectRegion(E, LCtx);
-State = markStatementsCorrespondingToConstructedObject(
-State, RTC->getConstructionContext(), LCtx, loc::MemRegionVal(TR));
-
+SVal Target;
+assert(RTC->getStmt() == Call.getOriginExpr());
+EvalCallOptions CallOpts; // FIXME: We won't really need those.
+std::tie(State, Target) =
+prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx,
+ RTC->getConstructionContext(), CallOpts);
+assert(Target.getAsRegion());
 // Invalidate the region so that it didn't look uninitialized. Don't notify
 // the checkers.
-State = State->invalidateRegions(TR, E, Count, LCtx,
+State = State->invalidateRegions(Target.getAsRegion(), E, Count, LCtx,
  /* CausedByPointerEscape=*/false, nullptr,
  &Call, nullptr);
 
-R = State->getSVal(TR, E->getType());
+R = State->getSVal(Target.castAs(), E->getType());
   } else {
 // Conjure a symbol if the return value is unknown.
 
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -110,13 +110,9 @@
   return LValue;
 }
 
-
-SVal ExprEngine::getLocationForConstructedObject(const CXXConstructExpr *CE,
- ExplodedNode *Pred,
- const ConstructionContext *CC,
- EvalCallOptions &CallOpts) {
-  const LocationContext *LCtx = Pred->getLocationContext();
-  ProgramStateRef State = Pred->getState();
+std::pair ExprEngine::prepareForObjectConstruction(
+const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
+const ConstructionContext *CC, EvalCallOptions &CallOpts) {
   MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
 
   // See if we're constructing an existing region by looking at the
@@ -129,8 +125,9 @@
   const auto *Var = cast(DS->getSingleDecl());
   SVal LValue = State->getLValue(Var, LCtx);
   QualType Ty = Var->getType();
-  return makeZeroElementRegion(State, LValue, Ty,
-   CallOpts.IsArrayCtorOrDtor);
+  return std::make_pair(
+  State,
+  makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor));
 }
 case ConstructionContext::SimpleConstructorInitializerKind: {
   const auto *ICC = cast(CC);
@@ -154,7 +151,7 @@
   QualType Ty = Field->getType();
   FieldVal = makeZeroElementRegion(State, FieldVal, Ty,
CallOpts.IsArrayCtorOrDtor);
-  return FieldVal;
+  return std::make_pair(State, FieldVal);
 }
 case ConstructionContext::NewAllocatedObjectKind: {
   if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
@@ -167,65 +164,97 @@
 // TODO: In fact, we need to call the constructor for every
 // allocated element, not just the first one!
 CallOpts.IsArrayCtorOrDtor = true;
-return loc::MemRegionVal(getStoreManager().GetElementZeroRegi

[PATCH] D47305: [analyzer] pr37270: Fix binding constructed object to DeclStmt when ConstructionContext is already lost.

2018-05-23 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, baloghadamsoftware.

As explained in http://lists.llvm.org/pipermail/cfe-dev/2018-May/058055.html, 
`findDirectConstructorForCurrentCFGElement()` was not working correctly in the 
current example and erroneously made us think that we've constructed into a 
dummy temporary region rather than into the variable. Instead, it was proposed 
to track it in the program state every time we are performing construction 
correctly.

Additionally this information will be used to maintain liveness of the 
variable's Store bindings, because previously an incorrect Environment binding 
of the target region to the construct-expression was used for that purpose. 
Such binding is incorrect because the constructor is a prvalue of an object 
type and should therefore have a NonLoc representing its symbolic value. 
Therefore the hack implemented by `isTemporaryPRValue()` can be safely removed.

`findDirectConstructorForCurrentCFGElement()` cannot be removed yet because it 
is also used for constructor member initializers for the same purpose. It 
doesn't seem to introduce bugs though.


Repository:
  rC Clang

https://reviews.llvm.org/D47305

Files:
  lib/StaticAnalyzer/Core/ExprEngineC.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  test/Analysis/cxx17-mandatory-elision.cpp

Index: test/Analysis/cxx17-mandatory-elision.cpp
===
--- test/Analysis/cxx17-mandatory-elision.cpp
+++ test/Analysis/cxx17-mandatory-elision.cpp
@@ -3,6 +3,26 @@
 
 void clang_analyzer_eval(bool);
 
+namespace variable_functional_cast_crash {
+
+struct A {
+  A(int) {}
+};
+
+void foo() {
+  A a = A(0);
+}
+
+struct B {
+  A a;
+  B(): a(A(0)) {}
+};
+
+} // namespace variable_functional_cast_crash
+
+
+namespace address_vector_tests {
+
 template  struct AddressVector {
   T *buf[10];
   int len;
@@ -56,3 +76,5 @@
   clang_analyzer_eval(v.buf[4] == &c); // expected-warning{{TRUE}}
 #endif
 }
+
+} // namespace address_vector_tests
Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -193,23 +193,6 @@
   return RuntimeCallee->getCanonicalDecl() != StaticDecl->getCanonicalDecl();
 }
 
-/// Returns true if the CXXConstructExpr \p E was intended to construct a
-/// prvalue for the region in \p V.
-///
-/// Note that we can't just test for rvalue vs. glvalue because
-/// CXXConstructExprs embedded in DeclStmts and initializers are considered
-/// rvalues by the AST, and the analyzer would like to treat them as lvalues.
-static bool isTemporaryPRValue(const CXXConstructExpr *E, SVal V) {
-  if (E->isGLValue())
-return false;
-
-  const MemRegion *MR = V.getAsRegion();
-  if (!MR)
-return false;
-
-  return isa(MR);
-}
-
 /// The call exit is simulated with a sequence of nodes, which occur between
 /// CallExitBegin and CallExitEnd. The following operations occur between the
 /// two program points:
@@ -269,11 +252,7 @@
   loc::MemRegionVal This =
 svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx);
   SVal ThisV = state->getSVal(This);
-
-  // If the constructed object is a temporary prvalue, get its bindings.
-  if (isTemporaryPRValue(CCE, ThisV))
-ThisV = state->getSVal(ThisV.castAs());
-
+  ThisV = state->getSVal(ThisV.castAs());
   state = state->BindExpr(CCE, callerCtx, ThisV);
 }
 
@@ -574,11 +553,7 @@
 }
   } else if (const CXXConstructorCall *C = dyn_cast(&Call)){
 SVal ThisV = C->getCXXThisVal();
-
-// If the constructed object is a temporary prvalue, get its bindings.
-if (isTemporaryPRValue(cast(E), ThisV))
-  ThisV = State->getSVal(ThisV.castAs());
-
+ThisV = State->getSVal(ThisV.castAs());
 return State->BindExpr(E, LCtx, ThisV);
   }
 
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -125,9 +125,11 @@
   const auto *Var = cast(DS->getSingleDecl());
   SVal LValue = State->getLValue(Var, LCtx);
   QualType Ty = Var->getType();
-  return std::make_pair(
-  State,
-  makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor));
+  LValue =
+  makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
+  State =
+  addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, LValue);
+  return std::make_pair(State, LValue);
 }
 case ConstructionContext::SimpleConstructorInitializerKind: {
   const auto *ICC = cast(CC);
Index: lib/StaticAnalyzer/Cor

[PATCH] D47350: [analyzer] Track class member initializer constructors path-sensitively within their construction context.

2018-05-24 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, baloghadamsoftware.

Same as https://reviews.llvm.org/D47305 but for `CXXCtorInitializer`-based 
constructors, based on the discussion in 
http://lists.llvm.org/pipermail/cfe-dev/2018-May/058055.html

Because these don't suffer from liveness bugs (member variables are born much 
earlier than their constructors are called and live much longer than stack 
locals), this is mostly a refactoring pass. It has minor observable side 
effects, but it will have way more visible effects when we enable C++17 
construction contexts because finding the direct constructor would be much 
harder.

Currently the observable effect is that we can preserve direct bindings to the 
object more often and we need to fall back to binding the lazy compound value 
of the initializer expression less often. Direct bindings are modeled better by 
the store. In the provided test case the default binding produced by 
trivial-copying `s` to `t.s` would overwrite the existing default binding to 
`t`. But if we instead preserve direct bindings, only bindings that correspond 
to `t.s` will be overwritten and the binding for `t.w` will remain untouched. 
This only works for C++17 because in C++11 the member variable is still modeled 
as a trivial copy from the temporary object, because there semantically *is* a 
temporary object, while in C++17 it is elided.


Repository:
  rC Clang

https://reviews.llvm.org/D47350

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  test/Analysis/cxx17-mandatory-elision.cpp

Index: test/Analysis/cxx17-mandatory-elision.cpp
===
--- test/Analysis/cxx17-mandatory-elision.cpp
+++ test/Analysis/cxx17-mandatory-elision.cpp
@@ -21,6 +21,36 @@
 } // namespace variable_functional_cast_crash
 
 
+namespace ctor_initializer {
+
+struct S {
+  int x, y, z;
+};
+
+struct T {
+  S s;
+  int w;
+  T(int w): s(), w(w) {}
+};
+
+class C {
+  T t;
+public:
+  C() : t(T(4)) {
+S s = {1, 2, 3};
+t.s = s;
+clang_analyzer_eval(t.w == 4);
+#if __cplusplus >= 201703L
+// expected-warning@-2{{TRUE}}
+#else
+// expected-warning@-4{{UNKNOWN}}
+#endif
+  }
+};
+
+} // namespace ctor_initializer
+
+
 namespace address_vector_tests {
 
 template  struct AddressVector {
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -153,6 +153,7 @@
   QualType Ty = Field->getType();
   FieldVal = makeZeroElementRegion(State, FieldVal, Ty,
CallOpts.IsArrayCtorOrDtor);
+  State = addObjectUnderConstruction(State, Init, LCtx, FieldVal);
   return std::make_pair(State, FieldVal);
 }
 case ConstructionContext::NewAllocatedObjectKind: {
@@ -272,35 +273,6 @@
   State, loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)));
 }
 
-const CXXConstructExpr *
-ExprEngine::findDirectConstructorForCurrentCFGElement() {
-  // Go backward in the CFG to see if the previous element (ignoring
-  // destructors) was a CXXConstructExpr. If so, that constructor
-  // was constructed directly into an existing region.
-  // This process is essentially the inverse of that performed in
-  // findElementDirectlyInitializedByCurrentConstructor().
-  if (currStmtIdx == 0)
-return nullptr;
-
-  const CFGBlock *B = getBuilderContext().getBlock();
-
-  unsigned int PreviousStmtIdx = currStmtIdx - 1;
-  CFGElement Previous = (*B)[PreviousStmtIdx];
-
-  while (Previous.getAs() && PreviousStmtIdx > 0) {
---PreviousStmtIdx;
-Previous = (*B)[PreviousStmtIdx];
-  }
-
-  if (Optional PrevStmtElem = Previous.getAs()) {
-if (auto *CtorExpr = dyn_cast(PrevStmtElem->getStmt())) {
-  return CtorExpr;
-}
-  }
-
-  return nullptr;
-}
-
 void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNode *Pred,
ExplodedNodeSet &destNodes) {
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -100,7 +100,68 @@
 
 // Keeps track of whether objects corresponding to the statement have been
 // fully constructed.
-typedef std::pair ConstructedObjectKey;
+
+/// ConstructedObjectKey is used for being able to find the path-sensitive
+/// memory region of a freshly constructed object while modeling the AST node
+/// that syntactically represents the object that is being constructed.
+/// Semantics of such nodes may sometimes require access to the region that's
+/// not otherwise

[PATCH] D47350: [analyzer] Track class member initializer constructors path-sensitively within their construction context.

2018-05-24 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 148502.
NoQ added a comment.

Add a forgotten FIXME to the test.


https://reviews.llvm.org/D47350

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  test/Analysis/cxx17-mandatory-elision.cpp

Index: test/Analysis/cxx17-mandatory-elision.cpp
===
--- test/Analysis/cxx17-mandatory-elision.cpp
+++ test/Analysis/cxx17-mandatory-elision.cpp
@@ -21,6 +21,37 @@
 } // namespace variable_functional_cast_crash
 
 
+namespace ctor_initializer {
+
+struct S {
+  int x, y, z;
+};
+
+struct T {
+  S s;
+  int w;
+  T(int w): s(), w(w) {}
+};
+
+class C {
+  T t;
+public:
+  C() : t(T(4)) {
+S s = {1, 2, 3};
+t.s = s;
+// FIXME: Should be TRUE in C++11 as well.
+clang_analyzer_eval(t.w == 4);
+#if __cplusplus >= 201703L
+// expected-warning@-2{{TRUE}}
+#else
+// expected-warning@-4{{UNKNOWN}}
+#endif
+  }
+};
+
+} // namespace ctor_initializer
+
+
 namespace address_vector_tests {
 
 template  struct AddressVector {
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -153,6 +153,7 @@
   QualType Ty = Field->getType();
   FieldVal = makeZeroElementRegion(State, FieldVal, Ty,
CallOpts.IsArrayCtorOrDtor);
+  State = addObjectUnderConstruction(State, Init, LCtx, FieldVal);
   return std::make_pair(State, FieldVal);
 }
 case ConstructionContext::NewAllocatedObjectKind: {
@@ -272,35 +273,6 @@
   State, loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)));
 }
 
-const CXXConstructExpr *
-ExprEngine::findDirectConstructorForCurrentCFGElement() {
-  // Go backward in the CFG to see if the previous element (ignoring
-  // destructors) was a CXXConstructExpr. If so, that constructor
-  // was constructed directly into an existing region.
-  // This process is essentially the inverse of that performed in
-  // findElementDirectlyInitializedByCurrentConstructor().
-  if (currStmtIdx == 0)
-return nullptr;
-
-  const CFGBlock *B = getBuilderContext().getBlock();
-
-  unsigned int PreviousStmtIdx = currStmtIdx - 1;
-  CFGElement Previous = (*B)[PreviousStmtIdx];
-
-  while (Previous.getAs() && PreviousStmtIdx > 0) {
---PreviousStmtIdx;
-Previous = (*B)[PreviousStmtIdx];
-  }
-
-  if (Optional PrevStmtElem = Previous.getAs()) {
-if (auto *CtorExpr = dyn_cast(PrevStmtElem->getStmt())) {
-  return CtorExpr;
-}
-  }
-
-  return nullptr;
-}
-
 void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNode *Pred,
ExplodedNodeSet &destNodes) {
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -100,7 +100,68 @@
 
 // Keeps track of whether objects corresponding to the statement have been
 // fully constructed.
-typedef std::pair ConstructedObjectKey;
+
+/// ConstructedObjectKey is used for being able to find the path-sensitive
+/// memory region of a freshly constructed object while modeling the AST node
+/// that syntactically represents the object that is being constructed.
+/// Semantics of such nodes may sometimes require access to the region that's
+/// not otherwise present in the program state, or to the very fact that
+/// the construction context was present and contained references to these
+/// AST nodes.
+class ConstructedObjectKey {
+  llvm::PointerUnion P;
+  const LocationContext *LC;
+
+  const void *getAnyASTNodePtr() const {
+if (const Stmt *S = getStmt())
+  return S;
+else
+  return getCXXCtorInitializer();
+  }
+
+public:
+  ConstructedObjectKey(
+  llvm::PointerUnion P,
+  const LocationContext *LC)
+  : P(P), LC(LC) {
+// This is the full list of statements that require additional actions when
+// encountered. This list may be expanded when new actions are implemented.
+assert(getCXXCtorInitializer() || isa(getStmt()) ||
+   isa(getStmt()) || isa(getStmt()) ||
+   isa(getStmt()));
+  }
+
+  const Stmt *getStmt() const {
+return P.dyn_cast();
+  }
+  const CXXCtorInitializer *getCXXCtorInitializer() const {
+return P.dyn_cast();
+  }
+  const LocationContext *getLocationContext() const {
+return LC;
+  }
+
+  void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) {
+OS << '(' << LC << ',' << getAnyASTNodePtr() << ") ";
+if (const Stmt *S = P.dyn_cast()) {
+  S->printPretty(OS, Helper, PP);
+} else {
+  const CXXCtorInitializer *I = P.get();
+  OS << I->getAnyMember()->getNameAsString();
+

[PATCH] D47351: [analyzer] Re-enable C++17-specific variable and member variable construction contexts.

2018-05-24 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, baloghadamsoftware.

The recent refactoring allows us to easily support C++17 "copy-elided" 
constructor syntax for variables and constructor-initializers. Similar 
constructors for return values are still to be supported.

In particular, the change of using path-sensitive state traits made our 
liveness problems go away. The `elision_on_ternary_op_branches` test was 
failing without that.

https://reviews.llvm.org/D47350 allowed us to support the C++17 destructor in 
`testCtorInitializer()`.


Repository:
  rC Clang

https://reviews.llvm.org/D47351

Files:
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  test/Analysis/cxx17-mandatory-elision.cpp

Index: test/Analysis/cxx17-mandatory-elision.cpp
===
--- test/Analysis/cxx17-mandatory-elision.cpp
+++ test/Analysis/cxx17-mandatory-elision.cpp
@@ -49,9 +49,64 @@
   }
 };
 
+
+struct A {
+  int x;
+  A(): x(0) {}
+  ~A() {}
+};
+
+struct B {
+  A a;
+  B() : a(A()) {}
+};
+
+void foo() {
+  B b;
+  clang_analyzer_eval(b.a.x == 0); // expected-warning{{TRUE}}
+}
+
 } // namespace ctor_initializer
 
 
+namespace elision_on_ternary_op_branches {
+class C1 {
+  int x;
+public:
+  C1(int x): x(x) {}
+  int getX() const { return x; }
+  ~C1();
+};
+
+class C2 {
+  int x;
+  int y;
+public:
+  C2(int x, int y): x(x), y(y) {}
+  int getX() const { return x; }
+  int getY() const { return y; }
+  ~C2();
+};
+
+void foo(int coin) {
+  C1 c1 = coin ? C1(1) : C1(2);
+  if (coin) {
+clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}}
+  } else {
+clang_analyzer_eval(c1.getX() == 2); // expected-warning{{TRUE}}
+  }
+  C2 c2 = coin ? C2(3, 4) : C2(5, 6);
+  if (coin) {
+clang_analyzer_eval(c2.getX() == 3); // expected-warning{{TRUE}}
+clang_analyzer_eval(c2.getY() == 4); // expected-warning{{TRUE}}
+  } else {
+clang_analyzer_eval(c2.getX() == 5); // expected-warning{{TRUE}}
+clang_analyzer_eval(c2.getY() == 6); // expected-warning{{TRUE}}
+  }
+}
+} // namespace elision_on_ternary_op_branches
+
+
 namespace address_vector_tests {
 
 template  struct AddressVector {
@@ -108,4 +163,68 @@
 #endif
 }
 
+class ClassWithDestructor {
+  AddressVector &v;
+
+public:
+  ClassWithDestructor(AddressVector &v) : v(v) {
+v.push(this);
+  }
+
+  ClassWithDestructor(ClassWithDestructor &&c) : v(c.v) { v.push(this); }
+  ClassWithDestructor(const ClassWithDestructor &c) : v(c.v) {
+v.push(this);
+  }
+
+  ~ClassWithDestructor() { v.push(this); }
+};
+
+void testVariable() {
+  AddressVector v;
+  {
+ClassWithDestructor c = ClassWithDestructor(v);
+  }
+#if __cplusplus >= 201703L
+  // 0. Construct the variable.
+  // 1. Destroy the variable.
+  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
+#else
+  // 0. Construct the temporary.
+  // 1. Construct the variable.
+  // 2. Destroy the temporary.
+  // 3. Destroy the variable.
+  clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}
+#endif
+}
+
+struct TestCtorInitializer {
+  ClassWithDestructor c;
+  TestCtorInitializer(AddressVector &v)
+: c(ClassWithDestructor(v)) {}
+};
+
+void testCtorInitializer() {
+  AddressVector v;
+  {
+TestCtorInitializer t(v);
+  }
+#if __cplusplus >= 201703L
+  // 0. Construct the member variable.
+  // 1. Destroy the member variable.
+  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
+#else
+  // 0. Construct the temporary.
+  // 1. Construct the member variable.
+  // 2. Destroy the temporary.
+  // 3. Destroy the member variable.
+  clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}
+#endif
+}
+
 } // namespace address_vector_tests
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -119,8 +119,9 @@
   // current construction context.
   if (CC) {
 switch (CC->getKind()) {
+case ConstructionContext::CXX17ElidedCopyVariableKind:
 case ConstructionContext::SimpleVariableKind: {
-  const auto *DSCC = cast(CC);
+  const auto *DSCC = cast(CC);
   const auto *DS = DSCC->getDeclStmt();
   const auto *Var = cast(DS->getSingleDecl());
   SVal LValue = State->getLValue(Var, LCtx);
@@ -131,6 +132,7 @@
   addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, LValue);

[PATCH] D46823: [analyzer] const init: handle non-explicit cases more accurately

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

Looks good!

Do you have commit access? I think you should get commit access.




Comment at: lib/StaticAnalyzer/Core/RegionStore.cpp:1650
+
+// If there is a list, but no init, it must be zero.
+if (i >= InitList->getNumInits())

r.stahl wrote:
> r.stahl wrote:
> > NoQ wrote:
> > > NoQ wrote:
> > > > Would this work correctly if the element is not of an integral or 
> > > > enumeration type? I think this needs an explicit check.
> > > What if we have an out-of-bounds access to a variable-length array? I 
> > > don't think it'd yield zero.
> > I'm getting "variable-sized object may not be initialized", so this case 
> > should not be possible.
> I'm having a hard time reproducing this either.
> 
> 
> ```
> struct S {
>   int a = 3;
> };
> S const sarr[2] = {};
> void definit() {
>   int i = 1;
>   clang_analyzer_dump(sarr[i].a); // expected-warning{{test}}
> }
> ```
> 
> results in a symbolic value, because makeZeroVal returns an empty SVal list 
> for arrays, records, vectors and complex types. Otherwise it just returns 
> UnknownVal (for example for not-yet-implemented floats).
> 
> Can you think of a case where this would be an issue?
Yup, sounds reasonable.



Comment at: lib/StaticAnalyzer/Core/RegionStore.cpp:1650-1652
+// If there is a list, but no init, it must be zero.
+if (i >= InitList->getNumInits())
+  return svalBuilder.makeZeroVal(R->getElementType());

NoQ wrote:
> r.stahl wrote:
> > r.stahl wrote:
> > > NoQ wrote:
> > > > NoQ wrote:
> > > > > Would this work correctly if the element is not of an integral or 
> > > > > enumeration type? I think this needs an explicit check.
> > > > What if we have an out-of-bounds access to a variable-length array? I 
> > > > don't think it'd yield zero.
> > > I'm getting "variable-sized object may not be initialized", so this case 
> > > should not be possible.
> > I'm having a hard time reproducing this either.
> > 
> > 
> > ```
> > struct S {
> >   int a = 3;
> > };
> > S const sarr[2] = {};
> > void definit() {
> >   int i = 1;
> >   clang_analyzer_dump(sarr[i].a); // expected-warning{{test}}
> > }
> > ```
> > 
> > results in a symbolic value, because makeZeroVal returns an empty SVal list 
> > for arrays, records, vectors and complex types. Otherwise it just returns 
> > UnknownVal (for example for not-yet-implemented floats).
> > 
> > Can you think of a case where this would be an issue?
> Yup, sounds reasonable.
Had a look. This indeed looks fine, but for a completely different reason. In 
fact structs don't appear in `getBindingForElement()` because they all go 
through `getBindingForStruct()` instead. Hopefully this does take care of other 
cornercases.

So your test case doesn't even trigger your code to be executed, neither would 
`S s = sarr[i]; clang_analyzer_dump(s.a);`.

Still, as far as i understand, `sarr` here should be zero-initialized, so i 
think the symbolic value can be improved upon, so we might as well add this as 
a FIXME test.


https://reviews.llvm.org/D46823



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


[PATCH] D45532: [StaticAnalyzer] Checker to find uninitialized fields after a constructor call

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Ok! Putting any remaining work into follow-up patches would be great indeed. 
Let's land this into `alpha.cplusplus` for now because i still haven't made up 
my mind for how to organize the proposed `bugprone` category (sorry!).




Comment at: lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp:223-225
+  ExplodedNode *Node = Context.generateNonFatalErrorNode(Context.getState());
+  if (!Node)
+return;

Szelethus wrote:
> NoQ wrote:
> > I suspect that a fatal error is better here. We don't want the user to 
> > receive duplicate report from other checkers that catch uninitialized 
> > values; just one report is enough.
> I think that would be a bad idea. For example, this checker shouts so loudly 
> while checking the LLVM project, it would practically halt the analysis of 
> the code, reducing the coverage, which means that checkers other then uninit 
> value checkers would "suffer" from it.
> 
> However, I also think that having multiple uninit reports for the same object 
> might be good, especially with this checker, as it would be very easy to see 
> where the problem originated from.
> 
> What do you think?
Well, i guess that's the reason to not use the checker on LLVM. Regardless of 
fatal/nonfatal warnings, enabling this checker on LLVM regularly would be a bad 
idea because it's unlikely that anybody will be able to fix all the false 
positives to make it usable. And for other projects that don't demonstrate many 
false positives, this shouldn't be a problem.

In order to indicate where the problem originates from, we have our bug 
reporter visitors that try their best to add such info directly to the report. 
In particular, @george.karpenkov's recent `NoStoreFuncVisitor` highlights 
functions in which a variable was //not// initialized but was probably expected 
to be. Not sure if it highlights constructors in its current shape, but that's 
definitely a better way to give this piece of information to the user because 
it doesn't make the user look for a different report to understand the current 
report.



Comment at: lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp:258-260
+Report->addNote(NoteBuf,
+PathDiagnosticLocation::create(FieldChain.getEndOfChain(),
+   
Context.getSourceManager()));

Szelethus wrote:
> NoQ wrote:
> > NoQ wrote:
> > > Szelethus wrote:
> > > > NoQ wrote:
> > > > > Aha, ok, got it, so you're putting the warning at the end of the 
> > > > > constructor and squeezing all uninitialized fields into a single 
> > > > > warning by presenting them as notes.
> > > > > 
> > > > > Because for now notes aren't supported everywhere (and aren't always 
> > > > > supported really well), i guess it'd be necessary to at least provide 
> > > > > an option to make note-less reports before the checker is enabled by 
> > > > > default everywhere. In this case notes contain critical pieces of 
> > > > > information and as such cannot be really discarded.
> > > > > 
> > > > > I'm not sure that notes are providing a better user experience here 
> > > > > than simply saying that "field x.y->z is never initialized". With 
> > > > > notes, the user will have to scroll around (at least in scan-build) 
> > > > > to find which field is uninitialized.
> > > > > 
> > > > > Notes will probably also not decrease the number of reports too much 
> > > > > because in most cases there will be just one uninitialized field. 
> > > > > Though i agree that when there is more than one report, the user will 
> > > > > be likely to deal with all fields at once rather than separately.
> > > > > 
> > > > > So it's a bit wonky.
> > > > > 
> > > > > We might want to enable one mode or another in the checker depending 
> > > > > on the analyzer output flag. Probably in the driver 
> > > > > (`RenderAnalyzerOptions()`).
> > > > > 
> > > > > It'd be a good idea to land the checker into alpha first and fix this 
> > > > > in a follow-up patch because this review is already overweight.
> > > > > [...]i guess it'd be necessary to at least provide an option to make 
> > > > > note-less reports before the checker is enabled by default 
> > > > > everywhere. In this case notes contain critical pieces of information 
> > > > > and as such cannot be really discarded.
> > > > This can already be achieved with `-analyzer-config 
> > > > notes-as-events=true`. There no good reason however not to make an 
> > > > additional flag that would emit warnings instead of notes.
> > > > > I'm not sure that notes are providing a better user experience here 
> > > > > than simply saying that "field x.y->z is never initialized". With 
> > > > > notes, the user will have to scroll around (at least in scan-build) 
> > > > > to find which field is uninitialized.
> > > > This checker had a previous implementation that emitted a warning for 
> > > > each uninitialized fiel

[PATCH] D35110: [Analyzer] Constraint Manager Negates Difference

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:265-276
+  const llvm::APSInt &from = i->From(), &to = i->To();
+  const llvm::APSInt &newFrom = (to.isMinSignedValue() ?
+ BV.getMaxValue(to) :
+ (to.isMaxSignedValue() ?
+  BV.getMinValue(to) :
+  BV.getValue(- to)));
+  const llvm::APSInt &newTo = (from.isMinSignedValue() ?

baloghadamsoftware wrote:
> NoQ wrote:
> > baloghadamsoftware wrote:
> > > NoQ wrote:
> > > > Hmm, wait a minute, is this actually correct?
> > > > 
> > > > For the range [-2³¹, -2³¹ + 1] over a 32-bit integer, the negated range 
> > > > will be [-2³¹, -2³¹] U [2³¹ - 1, 2³¹ - 1].
> > > > 
> > > > So there must be a place in the code where we take one range and add 
> > > > two ranges.
> > > The two ends of the range of the type usually stands for +/- infinity. If 
> > > we add the minimum of the type when negating a negative range, then we 
> > > lose the whole point of this transformation.
> > > 
> > > Example: If `A - B < 0`, then the range of `A - B` is `[-2³¹, -1]`, If we 
> > > negate this, and keep the `-2³¹` range end, then we get `[-2³¹, -2³¹]U[1, 
> > > 2³¹-1]`. However, this does not mean `B - A > 0`. If we make assumption 
> > > about this, we get two states instead of one, in the true state `A - B`'s 
> > > range is `[1, 2³¹-1]` and in the false state it is `[-2³¹, -2³¹]`. This 
> > > is surely not what we want.
> > Well, we can't turn math into something we want, it is what it is.
> > 
> > Iterator-related symbols are all planned to be within range [-2²⁹, -2²⁹], 
> > right? So if we subtract one such symbol from another, it's going to be in 
> > range [-2³⁰, 2³⁰]. Can we currently infer that? Or maybe we should make the 
> > iterator checker to enforce that separately? Because this range doesn't 
> > include -2³¹, so we won't have any problems with doing negation correctly.
> > 
> > So as usual i propose to get this code mathematically correct and then see 
> > if we can ensure correct behavior by enforcing reasonable constraints on 
> > our symbols.
> I agree that the code should do mathematically correct things, but what I 
> argue here is what math here means. Computer science is based on math, but it 
> is somewhat different because of finite ranges and overflows. So I initially 
> regarded the minimal and maximal values as infinity. Maybe that is not 
> correct. However, I am sure that negating `-2³¹` should never be `-2³¹`. This 
> is mathematically incorrect, and renders the whole calculation useless, since 
> the union of a positive range and a negative range is unsuitable for any 
> reasoning. I see two options here:
> 
> 1. Remove the extension when negating a range which ends at the maximal value 
> of the type. So the negated range begins at the minimal value plus one. 
> However, cut the range which begins at the minimal value of the type by one. 
> So the negated range ends at the maximal value, as in the current version in 
> the patch.
> 
> 2. Remove the extension as in 1. and disable the whole negation if we have 
> the range begins at the minimal value.
> 
> Iterator checkers are of course not affected because of the max/4 constraints.
> However, I am sure that negating `-2³¹` should never be `-2³¹`. This is 
> mathematically incorrect, and renders the whole calculation useless, since 
> the union of a positive range and a negative range is unsuitable for any 
> reasoning.

Well, that's how computers already work. And that's how all sorts of abstract 
algebra work as well, so this is totally mathematically correct. We promise to 
support the [[ https://en.wikipedia.org/wiki/Two's_complement | two's 
complement ]] semantics in the analyzer when it comes to signed integer 
overflows. Even though it's technically UB, most implementations follow this 
semantics and a lot of real-world applications explicitly rely on that. Also we 
cannot simply drop values from our constraint ranges in the general case 
because the values we drop might be the only valid values, and the assumption 
that at least one non-dropped value can definitely be taken is generally 
incorrect. Finding cornercases like this one is one of the strong sides of any 
static analysis: it is in fact our job to make the user aware of it if he 
doesn't understand overflow rules. If it cannot be said that the variable on a 
certain path is non-negative because it might as well be -2³¹, we should 
totally explore this possibility. If for a certain checker it brings no benefit 
because such value would be unlikely in certain circumstances, that checker is 
free to cut off the respective paths, but the core should perform operations 
precisely. I don't think we have much room for personal preferences here.


https://reviews.llvm.org/D35110



___

[PATCH] D47155: [analyzer] Reduce simplifySVal complexity threshold further.

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 148681.
NoQ added a comment.

Optimize `simplifySVal()` instead of reducing the threshold.

I'll address the memoization separately.


https://reviews.llvm.org/D47155

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
  lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
  test/Analysis/hangs.c


Index: test/Analysis/hangs.c
===
--- /dev/null
+++ test/Analysis/hangs.c
@@ -0,0 +1,30 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker core -verify %s
+
+// expected-no-diagnostics
+
+// Stuff that used to hang.
+
+int g();
+
+int f(int y) {
+  return y + g();
+}
+
+int produce_a_very_large_symbol(int x) {
+  return f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(
+ f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(x;
+}
+
+void produce_an_exponentially_exploding_symbol(int x, int y) {
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+}
Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
===
--- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -1222,6 +1222,10 @@
 ProgramStateRef State;
 SValBuilder &SVB;
 
+static bool isUnchanged(SymbolRef Sym, SVal Val) {
+  return Sym == Val.getAsSymbol();
+}
+
   public:
 Simplifier(ProgramStateRef State)
 : State(State), SVB(State->getStateManager().getSValBuilder()) {}
@@ -1231,15 +1235,16 @@
   SVB.getKnownValue(State, nonloc::SymbolVal(S)))
 return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I)
 : (SVal)SVB.makeIntVal(*I);
-  return Loc::isLocType(S->getType()) ? (SVal)SVB.makeLoc(S) 
-  : nonloc::SymbolVal(S);
+  return SVB.makeSymbolVal(S);
 }
 
 // TODO: Support SymbolCast. Support IntSymExpr when/if we actually
 // start producing them.
 
 SVal VisitSymIntExpr(const SymIntExpr *S) {
   SVal LHS = Visit(S->getLHS());
+  if (isUnchanged(S->getLHS(), LHS))
+return SVB.makeSymbolVal(S);
   SVal RHS;
   // By looking at the APSInt in the right-hand side of S, we cannot
   // figure out if it should be treated as a Loc or as a NonLoc.
@@ -1264,6 +1269,8 @@
 SVal VisitSymSymExpr(const SymSymExpr *S) {
   SVal LHS = Visit(S->getLHS());
   SVal RHS = Visit(S->getRHS());
+  if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS))
+return SVB.makeSymbolVal(S);
   return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
 }
 
Index: include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
@@ -367,6 +367,15 @@
 return loc::ConcreteInt(BasicVals.getValue(integer));
   }
 
+  /// Make an SVal that represents the given symbol. This follows the 
convention
+  /// of representing Loc-type symbols (symbolic pointers and references)
+  /// as Loc values wrapping the symbol rather than as plain symbol values.
+  SVal makeSymbolVal(SymbolRef Sym) {
+if (Loc::isLocType(Sym->getType()))
+  return makeLoc(Sym);
+return nonloc::SymbolVal(Sym);
+  }
+
   /// Return a memory region for the 'this' object reference.
   loc::MemRegionVal getCXXThis(const CXXMethodDecl *D,
const StackFrameContext *SFC);


Index: test/Analysis/hangs.c
===
--- /dev/null
+++ test/Analysis/hangs.c
@@ -0,0 +1,30 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker core -verify %s
+
+// expected-no-diagnostics
+
+// Stuff that used to hang.
+
+int g();
+
+int f(int y) {
+  return y + g();
+}
+
+int produce_a_very_large_symbol(int x) {
+  return f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(
+ f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(x;
+}
+
+void produce_an_exponentially_exploding_symbol(int x, int y) {
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+}
Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
===
--- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -1222,6 +1222,10 @@
 ProgramStateRef State;
 SValBuilder &SVB;
 
+static bool isUnc

[PATCH] D47155: [analyzer] Reduce simplifySVal complexity threshold further.

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

I only essentially did one optimization - introduce a short path that returns 
the original value if visiting its sub-values changed nothing, which is a 
relatively common case. The reason it works though is that `evalBinOp()` will 
be called later to combine the sub-values, which may in turn call 
`simplifySVal()` again, which is clearly unwanted.




Comment at: test/Analysis/hangs.c:18-30
+void produce_an_exponentially_exploding_symbol(int x, int y) {
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();

This currently finishes in 1 second on my machine. This test, unlike the 
original test, is easy to experiment with.


https://reviews.llvm.org/D47155



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


[PATCH] D47155: [analyzer] Reduce simplifySVal complexity threshold further.

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 148684.
NoQ added a comment.

Add an explicit brute-force protection against re-entering `simplifySVal()`. 
Remove the threshold completely.


https://reviews.llvm.org/D47155

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
  lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
  test/Analysis/hangs.c

Index: test/Analysis/hangs.c
===
--- /dev/null
+++ test/Analysis/hangs.c
@@ -0,0 +1,30 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker core -verify %s
+
+// expected-no-diagnostics
+
+// Stuff that used to hang.
+
+int g();
+
+int f(int y) {
+  return y + g();
+}
+
+int produce_a_very_large_symbol(int x) {
+  return f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(
+ f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(x;
+}
+
+void produce_an_exponentially_exploding_symbol(int x, int y) {
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+  x += y; y += x + g();
+}
Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
===
--- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -1222,6 +1222,10 @@
 ProgramStateRef State;
 SValBuilder &SVB;
 
+static bool isUnchanged(SymbolRef Sym, SVal Val) {
+  return Sym == Val.getAsSymbol();
+}
+
   public:
 Simplifier(ProgramStateRef State)
 : State(State), SVB(State->getStateManager().getSValBuilder()) {}
@@ -1231,15 +1235,16 @@
   SVB.getKnownValue(State, nonloc::SymbolVal(S)))
 return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I)
 : (SVal)SVB.makeIntVal(*I);
-  return Loc::isLocType(S->getType()) ? (SVal)SVB.makeLoc(S) 
-  : nonloc::SymbolVal(S);
+  return SVB.makeSymbolVal(S);
 }
 
 // TODO: Support SymbolCast. Support IntSymExpr when/if we actually
 // start producing them.
 
 SVal VisitSymIntExpr(const SymIntExpr *S) {
   SVal LHS = Visit(S->getLHS());
+  if (isUnchanged(S->getLHS(), LHS))
+return SVB.makeSymbolVal(S);
   SVal RHS;
   // By looking at the APSInt in the right-hand side of S, we cannot
   // figure out if it should be treated as a Loc or as a NonLoc.
@@ -1264,6 +1269,8 @@
 SVal VisitSymSymExpr(const SymSymExpr *S) {
   SVal LHS = Visit(S->getLHS());
   SVal RHS = Visit(S->getRHS());
+  if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS))
+return SVB.makeSymbolVal(S);
   return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
 }
 
@@ -1274,13 +1281,20 @@
 SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) {
   // Simplification is much more costly than computing complexity.
   // For high complexity, it may be not worth it.
-  if (V.getSymbol()->computeComplexity() > 100)
-return V;
   return Visit(V.getSymbol());
 }
 
 SVal VisitSVal(SVal V) { return V; }
   };
 
-  return Simplifier(State).Visit(V);
+  // A crude way of preventing this function from calling itself from evalBinOp.
+  static bool isReentering = false;
+  if (isReentering)
+return V;
+
+  isReentering = true;
+  SVal SimplifiedV = Simplifier(State).Visit(V);
+  isReentering = false;
+
+  return SimplifiedV;
 }
Index: include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
@@ -367,6 +367,15 @@
 return loc::ConcreteInt(BasicVals.getValue(integer));
   }
 
+  /// Make an SVal that represents the given symbol. This follows the convention
+  /// of representing Loc-type symbols (symbolic pointers and references)
+  /// as Loc values wrapping the symbol rather than as plain symbol values.
+  SVal makeSymbolVal(SymbolRef Sym) {
+if (Loc::isLocType(Sym->getType()))
+  return makeLoc(Sym);
+return nonloc::SymbolVal(Sym);
+  }
+
   /// Return a memory region for the 'this' object reference.
   loc::MemRegionVal getCXXThis(const CXXMethodDecl *D,
const StackFrameContext *SFC);
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D47303: [analyzer] NFC: Merge object construction related state traits into one.

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:377
+  if (!State->contains(Key)) {
+return State->set(Key, V);
   }

george.karpenkov wrote:
> nitpick: most C++ containers eliminate the need for two lookups by allowing 
> the `get` method to return a reference. I'm not sure whether this can be done 
> here though.
It's likely to be harder than usual because one doesn't simply obtain a 
non-const reference to an object from an immutable map.

That's said, it's quite unimportant what do we do if the object is already 
there.


https://reviews.llvm.org/D47303



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


[PATCH] D47303: [analyzer] NFC: Merge object construction related state traits into one.

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 148692.
NoQ marked 3 inline comments as done.
NoQ added a comment.

Fix review comments.


https://reviews.llvm.org/D47303

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -288,8 +288,8 @@
   AllocV, CNE->getType(),
   getContext().getPointerType(getContext().VoidTy));
 
-  state =
-  setCXXNewAllocatorValue(state, CNE, calleeCtx->getParent(), AllocV);
+  state = addObjectUnderConstruction(state, CNE, calleeCtx->getParent(),
+ AllocV);
 }
   }
 
@@ -354,8 +354,9 @@
  /*WasInlined=*/true);
   for (auto I : DstPostPostCallCallback) {
 getCheckerManager().runCheckersForNewAllocator(
-CNE, getCXXNewAllocatorValue(I->getState(), CNE,
- calleeCtx->getParent()),
+CNE,
+*getObjectUnderConstruction(I->getState(), CNE,
+calleeCtx->getParent()),
 DstPostCall, I, *this,
 /*WasInlined=*/true);
   }
@@ -588,8 +589,8 @@
 // Conjure a temporary if the function returns an object by value.
 MemRegionManager &MRMgr = svalBuilder.getRegionManager();
 const CXXTempObjectRegion *TR = MRMgr.getCXXTempObjectRegion(E, LCtx);
-State = addAllNecessaryTemporaryInfo(State, RTC->getConstructionContext(),
- LCtx, TR);
+State = markStatementsCorrespondingToConstructedObject(
+State, RTC->getConstructionContext(), LCtx, loc::MemRegionVal(TR));
 
 // Invalidate the region so that it didn't look uninitialized. Don't notify
 // the checkers.
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -111,11 +111,10 @@
 }
 
 
-const MemRegion *
-ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
-  ExplodedNode *Pred,
-  const ConstructionContext *CC,
-  EvalCallOptions &CallOpts) {
+SVal ExprEngine::getLocationForConstructedObject(const CXXConstructExpr *CE,
+ ExplodedNode *Pred,
+ const ConstructionContext *CC,
+ EvalCallOptions &CallOpts) {
   const LocationContext *LCtx = Pred->getLocationContext();
   ProgramStateRef State = Pred->getState();
   MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
@@ -130,9 +129,8 @@
   const auto *Var = cast(DS->getSingleDecl());
   SVal LValue = State->getLValue(Var, LCtx);
   QualType Ty = Var->getType();
-  LValue =
-  makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
-  return LValue.getAsRegion();
+  return makeZeroElementRegion(State, LValue, Ty,
+   CallOpts.IsArrayCtorOrDtor);
 }
 case ConstructionContext::SimpleConstructorInitializerKind: {
   const auto *ICC = cast(CC);
@@ -156,25 +154,26 @@
   QualType Ty = Field->getType();
   FieldVal = makeZeroElementRegion(State, FieldVal, Ty,
CallOpts.IsArrayCtorOrDtor);
-  return FieldVal.getAsRegion();
+  return FieldVal;
 }
 case ConstructionContext::NewAllocatedObjectKind: {
   if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
 const auto *NECC = cast(CC);
 const auto *NE = NECC->getCXXNewExpr();
-// TODO: Detect when the allocator returns a null pointer.
-// Constructor shall not be called in this case.
-if (const SubRegion *MR = dyn_cast_or_null(
-getCXXNewAllocatorValue(State, NE, LCtx).getAsRegion())) {
+SVal V = *getObjectUnderConstruction(State, NE, LCtx);
+if (const SubRegion *MR =
+dyn_cast_or_null(V.getAsRegion())) {
   if (NE->isArray()) {
 // TODO: In fact, we need to call the constructor for every
 // allocated element, not just the first one!
 CallOpts.IsArrayCtorOrDtor = true;
-return getStoreManager().GetElementZeroRegion(
-MR, NE->getType()->getPointeeType());
+return loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
+MR, NE->getType()->getPointeeType()));
  

[PATCH] D41881: [analyzer] Flag bcmp, bcopy and bzero as obsolete

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Ouch, this one really got out of hand. Sorry.


Repository:
  rC Clang

https://reviews.llvm.org/D41881



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


[PATCH] D47402: [analyzer] Improve simplifySVal performance further.

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, baloghadamsoftware.

Memoize `SValBuilder::simplifySVal()` so that it didn't try to re-simplify the 
same symbolic expression in the same program state.

Speeds up the analysis by ~25% on the artificial test in 
`test/Analysis/hangs.c`.


Repository:
  rC Clang

https://reviews.llvm.org/D47402

Files:
  lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp


Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
===
--- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -1222,6 +1222,12 @@
 ProgramStateRef State;
 SValBuilder &SVB;
 
+// Cache results for the lifetime of the Simplifier. Results change every
+// time new constraints are added to the program state, which is the whole
+// point of simplifying, and for that very reason it's pointless to 
maintain
+// the same cache for the duration of the whole analysis.
+llvm::DenseMap Cached;
+
 static bool isUnchanged(SymbolRef Sym, SVal Val) {
   return Sym == Val.getAsSymbol();
 }
@@ -1242,9 +1248,16 @@
 // start producing them.
 
 SVal VisitSymIntExpr(const SymIntExpr *S) {
+  auto I = Cached.find(S);
+  if (I != Cached.end())
+return I->second;
+
   SVal LHS = Visit(S->getLHS());
-  if (isUnchanged(S->getLHS(), LHS))
-return SVB.makeSymbolVal(S);
+  if (isUnchanged(S->getLHS(), LHS)) {
+SVal V = SVB.makeSymbolVal(S);
+Cached[S] = V;
+return V;
+  }
   SVal RHS;
   // By looking at the APSInt in the right-hand side of S, we cannot
   // figure out if it should be treated as a Loc or as a NonLoc.
@@ -1263,15 +1276,27 @@
   } else {
 RHS = SVB.makeIntVal(S->getRHS());
   }
-  return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+
+  SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+  Cached[S] = V;
+  return V;
 }
 
 SVal VisitSymSymExpr(const SymSymExpr *S) {
+  auto I = Cached.find(S);
+  if (I != Cached.end())
+return I->second;
+
   SVal LHS = Visit(S->getLHS());
   SVal RHS = Visit(S->getRHS());
-  if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS))
-return SVB.makeSymbolVal(S);
-  return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+  if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS)) {
+SVal V = SVB.makeSymbolVal(S);
+Cached[S] = V;
+return V;
+  }
+  SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+  Cached[S] = V;
+  return V;
 }
 
 SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); }


Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
===
--- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -1222,6 +1222,12 @@
 ProgramStateRef State;
 SValBuilder &SVB;
 
+// Cache results for the lifetime of the Simplifier. Results change every
+// time new constraints are added to the program state, which is the whole
+// point of simplifying, and for that very reason it's pointless to maintain
+// the same cache for the duration of the whole analysis.
+llvm::DenseMap Cached;
+
 static bool isUnchanged(SymbolRef Sym, SVal Val) {
   return Sym == Val.getAsSymbol();
 }
@@ -1242,9 +1248,16 @@
 // start producing them.
 
 SVal VisitSymIntExpr(const SymIntExpr *S) {
+  auto I = Cached.find(S);
+  if (I != Cached.end())
+return I->second;
+
   SVal LHS = Visit(S->getLHS());
-  if (isUnchanged(S->getLHS(), LHS))
-return SVB.makeSymbolVal(S);
+  if (isUnchanged(S->getLHS(), LHS)) {
+SVal V = SVB.makeSymbolVal(S);
+Cached[S] = V;
+return V;
+  }
   SVal RHS;
   // By looking at the APSInt in the right-hand side of S, we cannot
   // figure out if it should be treated as a Loc or as a NonLoc.
@@ -1263,15 +1276,27 @@
   } else {
 RHS = SVB.makeIntVal(S->getRHS());
   }
-  return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+
+  SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+  Cached[S] = V;
+  return V;
 }
 
 SVal VisitSymSymExpr(const SymSymExpr *S) {
+  auto I = Cached.find(S);
+  if (I != Cached.end())
+return I->second;
+
   SVal LHS = Visit(S->getLHS());
   SVal RHS = Visit(S->getRHS());
-  if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS))
-return SVB.makeSymbolVal(S);
-  return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+  if (isUnchanged(S->getL

[PATCH] D47402: [analyzer] Improve simplifySVal performance further.

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

The remaining slowness is in `removeDead()`. It's going to be fun to optimize. 
Some parts of it are already memoized (eg. the live set in `SymbolReaper`).

Memory usage on the artificial test seems stable.


Repository:
  rC Clang

https://reviews.llvm.org/D47402



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


[PATCH] D47405: [analyzer] Re-enable C++17-specific return value constructors.

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, baloghadamsoftware.

The refactoring conducted in https://reviews.llvm.org/D47304 made it easy for 
the analyzer to find the target region for the constructor across multiple 
stack frames. We ascend to the parent stack frame recursively until we find a 
construction context that doesn't represent yet another return value of a 
function. This is the semantics of copy elision in return statements that 
return the object by value: they return it directly to a memory region (eg., a 
variable) that may be located a few (indefinitely many) stack frames above the 
statement. In particular, this is how return statements work in C++17 where 
copy elision is mandatory. This commit enables this feature for the AST of 
C++17, but extra work is necessary to perform copy elision in the AST produced 
by older language standard modes.


Repository:
  rC Clang

https://reviews.llvm.org/D47405

Files:
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  test/Analysis/cxx17-mandatory-elision.cpp

Index: test/Analysis/cxx17-mandatory-elision.cpp
===
--- test/Analysis/cxx17-mandatory-elision.cpp
+++ test/Analysis/cxx17-mandatory-elision.cpp
@@ -150,9 +150,8 @@
   ClassWithoutDestructor c = make3(v);
 
 #if __cplusplus >= 201703L
-  // FIXME: Both should be TRUE.
   clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
-  clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{FALSE}}
+  clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{TRUE}}
 #else
   clang_analyzer_eval(v.len == 5); // expected-warning{{TRUE}}
   clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}}
@@ -183,6 +182,13 @@
   AddressVector v;
   {
 ClassWithDestructor c = ClassWithDestructor(v);
+// Check if the last destructor is an automatic destructor.
+// A temporary destructor would have fired by now.
+#if __cplusplus >= 201703L
+clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
+#else
+clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
+#endif
   }
 #if __cplusplus >= 201703L
   // 0. Construct the variable.
@@ -210,6 +216,13 @@
   AddressVector v;
   {
 TestCtorInitializer t(v);
+// Check if the last destructor is an automatic destructor.
+// A temporary destructor would have fired by now.
+#if __cplusplus >= 201703L
+clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
+#else
+clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
+#endif
   }
 #if __cplusplus >= 201703L
   // 0. Construct the member variable.
@@ -227,4 +240,53 @@
 #endif
 }
 
+
+ClassWithDestructor make1(AddressVector &v) {
+  return ClassWithDestructor(v);
+}
+ClassWithDestructor make2(AddressVector &v) {
+  return make1(v);
+}
+ClassWithDestructor make3(AddressVector &v) {
+  return make2(v);
+}
+
+void testMultipleReturnsWithDestructors() {
+  AddressVector v;
+  {
+ClassWithDestructor c = make3(v);
+// Check if the last destructor is an automatic destructor.
+// A temporary destructor would have fired by now.
+#if __cplusplus >= 201703L
+clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
+#else
+clang_analyzer_eval(v.len == 9); // expected-warning{{TRUE}}
+#endif
+  }
+
+#if __cplusplus >= 201703L
+  // 0. Construct the variable. Yes, constructor in make1() constructs
+  //the variable 'c'.
+  // 1. Destroy the variable.
+  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
+#else
+  // 0. Construct the temporary in make1().
+  // 1. Construct the temporary in make2().
+  // 2. Destroy the temporary in make1().
+  // 3. Construct the temporary in make3().
+  // 4. Destroy the temporary in make2().
+  // 5. Construct the temporary here.
+  // 6. Destroy the temporary in make3().
+  // 7. Construct the variable.
+  // 8. Destroy the temporary here.
+  // 9. Destroy the variable.
+  clang_analyzer_eval(v.len == 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[5] == v.buf[8]); // expected-warning{{TRUE}}
+  clang_analyzer_eval(v.buf[7] == v.buf[9]); // expected-warning{{TRUE}}
+#endif
+}
 } // namespace address_vector_tests
Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -180,7 +180,8 @@
   }
   break;
 }
-case ConstructionContext::SimpleReturnedValueKind: {
+case ConstructionContext::SimpleReturnedValueKind:
+case ConstructionContext::C

[PATCH] D47405: [analyzer] Re-enable C++17-specific return value constructors.

2018-05-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: test/Analysis/cxx17-mandatory-elision.cpp:185-191
+// Check if the last destructor is an automatic destructor.
+// A temporary destructor would have fired by now.
+#if __cplusplus >= 201703L
+clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
+#else
+clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
+#endif

This sounded like a good thing to check, so i added such checks in a few more 
places.


Repository:
  rC Clang

https://reviews.llvm.org/D47405



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


[PATCH] D47135: [analyzer] A checker for dangling internal buffer pointers in C++

2018-05-27 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.

Looks great, thanks! I think you should add a check for UnknownVal and commit.




Comment at: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp:65
+  if (Call.isCalled(CStrFn)) {
+SymbolRef RawPtr = Call.getReturnValue().getAsSymbol();
+State = State->set(TypedR, RawPtr);

xazax.hun wrote:
> xazax.hun wrote:
> > I wonder if we can always get a symbol.
> > I can think of two cases when the call above could fail:
> > * Non-standard implementation that does not return a pointer
> > * The analyzer able to inline stuff and the returned value is a constant (a 
> > specific address that is shared between all empty strings in some 
> > implementation?)
> > 
> > Even though I do find any of the above likely. @NoQ what do you think? Does 
> > this worth a check?
> Sorry for the spam. Unfortunately, it is not possible to edit the comments.
> I do *not* find any of the above likely.
We should almost always check if any value is an `UnknownVal`. The engine 
simply doesn't give any guarantees: it can give up any time and fall back to 
`UnknownVal`.



Comment at: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp:73
+if (State->contains(TypedR)) {
+  const SymbolRef *StrBufferPtr = State->get(TypedR);
+  const Expr *Origin = Call.getOriginExpr();

xazax.hun wrote:
> xazax.hun wrote:
> > What if no symbol is associated with the region? Won't this return null 
> > that we dereference later on?
> Oh, never mind this one, I did not notice the `contains` call above.
The interesting part here is what happens if no expression is associated with 
the call. For instance, if the call is an automatic destructor at the end of 
scope. I hope it works, but i'm not sure how Origin is used.



Comment at: lib/StaticAnalyzer/Checkers/MallocChecker.cpp:1661
+  case AF_CXXNewArray:
+  case AF_InternalBuffer: {
 if (IsALeakCheck) {

rnkovacs wrote:
> Is tying this new family to NewDeleteChecker reasonable? I did it because it 
> was NewDelete that warned naturally before creating the new 
> `AllocationFamily`. Should I introduce a new checker kind in MallocChecker 
> for this purpose instead?
Uhm, this code is weird.

I think you can add an "external" ("associated", "plugin") CheckKind that 
always warns.


https://reviews.llvm.org/D47135



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


[PATCH] D47417: [analyzer] Add missing state transition in IteratorChecker

2018-05-27 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Nice catch!




Comment at: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp:399
+
+  C.addTransition(State);
 }

MTC wrote:
> I have two questions may need @NoQ or @xazax.hun who is more familiar with 
> the analyzer engine help to answer.
> 
>   - `State` may not change at all, do we need a check here like [[ 
> https://github.com/llvm-mirror/clang/blob/master/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp#L227
>  | if (state != originalState) ]]
>   - A more basic problem is that do we need `originalState = State` trick. It 
> seems that `addTransitionImpl()` has a check about same state transition, see 
> [[ 
> https://github.com/llvm-mirror/clang/blob/master/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h#L339
>  | addTransitionImp() ]].
> 
> Thanks in advance!
> 
> 
> 
> It seems that `addTransitionImpl()` has a check about same state transition, 
> see `addTransitionImp()`.

Yep, you pretty much answered your question. The check in the checker code is 
unnecessary.


Repository:
  rC Clang

https://reviews.llvm.org/D47417



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


[PATCH] D47416: [analyzer] Clean up the program state map of DanglingInternalBufferChecker

2018-05-27 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Yep, great stuff!

I guess, please add a comment on incomplete destructor support in the CFG 
and/or analyzer core that may cause the region to be never cleaned up. Or 
perform an extra cleanup pass - not sure if it's worth it, but it should be 
easy and safe.




Comment at: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp:94
+  RawPtrMapTy RPM = State->get();
+  for (const auto Region : RPM) {
+if (SymReaper.isDead(Region.second))

It's kinda weird that something that's not a `const MemRegion *` is called 
"Region".

I'd rather use a neutral term like "Item".


Repository:
  rC Clang

https://reviews.llvm.org/D47416



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


[PATCH] D47451: [analyzer] Remove the redundant check about same state transition in `ArrayBoundCheckerV2.cpp`.

2018-05-29 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.
This revision is now accepted and ready to land.

Thx!


Repository:
  rC Clang

https://reviews.llvm.org/D47451



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


[PATCH] D35110: [Analyzer] Constraint Manager Negates Difference

2018-05-29 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:191
+}
+if (from.isMinSignedValue()) {
+  F.add(newRanges, Range(BV.getMinValue(from), BV.getMinValue(from)));

We'll also need to merge the two adjacent segments if the original range had 
both a [MinSingedValue, MinSignedValue] and a [X, MaxSignedValue]:

{F6287707}

Because our immutable sets are sorted, you can conduct the check for the first 
and the last segment separately.

I think this code needs comments because even though it's short it's pretty 
hard to get right.



Comment at: lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:192
+if (from.isMinSignedValue()) {
+  F.add(newRanges, Range(BV.getMinValue(from), BV.getMinValue(from)));
+}

Return value of `add` seems to be accidentally discarded here.

I guess i'll look into adding an `__attribute__((warn_unused_result))` to these 
functions, because it's a super common bug.


https://reviews.llvm.org/D35110



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


[PATCH] D35110: [Analyzer] Constraint Manager Negates Difference

2018-05-29 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/RangeConstraintManager.cpp:192
+if (from.isMinSignedValue()) {
+  F.add(newRanges, Range(BV.getMinValue(from), BV.getMinValue(from)));
+}

NoQ wrote:
> Return value of `add` seems to be accidentally discarded here.
> 
> I guess i'll look into adding an `__attribute__((warn_unused_result))` to 
> these functions, because it's a super common bug.
Also tests would have saved us.


https://reviews.llvm.org/D35110



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


[PATCH] D47499: [analyzer] Annotate program state update methods with LLVM_NODISCARD.

2018-05-29 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs, baloghadamsoftware.
Herald added a subscriber: cfe-commits.

A follow-up to https://reviews.llvm.org/D47496.

This would warn the developer on the very common bug that consists in writing, 
eg., `State->set(Key, Value)` instead of `State = State->set(Key, 
Value)`. Because in the first snippet the updated `State` object is discarded 
while the original object remains unchanged (because it's, well, immutable), 
which isn't what you ever want to do.

For now no such bugs were found in the analyzer, but i made these mistakes 
myself knowingly and i see many such bugs during code review.


Repository:
  rC Clang

https://reviews.llvm.org/D47499

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h

Index: include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
===
--- include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -177,38 +177,38 @@
   ///
   /// This returns a new state with the added constraint on \p cond.
   /// If no new state is feasible, NULL is returned.
-  ProgramStateRef assume(DefinedOrUnknownSVal cond, bool assumption) const;
+  LLVM_NODISCARD ProgramStateRef assume(DefinedOrUnknownSVal cond,
+bool assumption) const;
 
   /// Assumes both "true" and "false" for \p cond, and returns both
   /// corresponding states (respectively).
   ///
   /// This is more efficient than calling assume() twice. Note that one (but not
   /// both) of the returned states may be NULL.
-  std::pair
+  LLVM_NODISCARD std::pair
   assume(DefinedOrUnknownSVal cond) const;
 
-  ProgramStateRef assumeInBound(DefinedOrUnknownSVal idx,
-   DefinedOrUnknownSVal upperBound,
-   bool assumption,
-   QualType IndexType = QualType()) const;
+  LLVM_NODISCARD ProgramStateRef
+  assumeInBound(DefinedOrUnknownSVal idx, DefinedOrUnknownSVal upperBound,
+bool assumption, QualType IndexType = QualType()) const;
 
   /// Assumes that the value of \p Val is bounded with [\p From; \p To]
   /// (if \p assumption is "true") or it is fully out of this range
   /// (if \p assumption is "false").
   ///
   /// This returns a new state with the added constraint on \p cond.
   /// If no new state is feasible, NULL is returned.
-  ProgramStateRef assumeInclusiveRange(DefinedOrUnknownSVal Val,
-   const llvm::APSInt &From,
-   const llvm::APSInt &To,
-   bool assumption) const;
+  LLVM_NODISCARD ProgramStateRef assumeInclusiveRange(DefinedOrUnknownSVal Val,
+  const llvm::APSInt &From,
+  const llvm::APSInt &To,
+  bool assumption) const;
 
   /// Assumes given range both "true" and "false" for \p Val, and returns both
   /// corresponding states (respectively).
   ///
   /// This is more efficient than calling assume() twice. Note that one (but not
   /// both) of the returned states may be NULL.
-  std::pair
+  LLVM_NODISCARD std::pair
   assumeInclusiveRange(DefinedOrUnknownSVal Val, const llvm::APSInt &From,
const llvm::APSInt &To) const;
 
@@ -232,30 +232,32 @@
 
   /// Create a new state by binding the value 'V' to the statement 'S' in the
   /// state's environment.
-  ProgramStateRef BindExpr(const Stmt *S, const LocationContext *LCtx,
-   SVal V, bool Invalidate = true) const;
+  LLVM_NODISCARD ProgramStateRef BindExpr(const Stmt *S,
+  const LocationContext *LCtx, SVal V,
+  bool Invalidate = true) const;
 
-  ProgramStateRef bindLoc(Loc location,
-  SVal V,
-  const LocationContext *LCtx,
-  bool notifyChanges = true) const;
+  LLVM_NODISCARD ProgramStateRef bindLoc(Loc location, SVal V,
+ const LocationContext *LCtx,
+ bool notifyChanges = true) const;
 
-  ProgramStateRef bindLoc(SVal location, SVal V, const LocationContext *LCtx) const;
+  LLVM_NODISCARD ProgramStateRef bindLoc(SVal location, SVal V,
+ const LocationContext *LCtx) const;
 
   /// Initializes the region of memory represented by \p loc with an initial
   /// value. Once initialized, all values loaded from any sub-regions of that
   /// region will be equal to \p V, unless overwritten later by the program.
   /// This method should not be used on regions that are alrea

[PATCH] D45517: [analyzer] False positive refutation with Z3

2018-05-29 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp:1292
+  Constraints =
+  Z3Expr::fromBinOp(Constraints, BO_LOr, SymRange, IsSignedTy);
+}

george.karpenkov wrote:
> I'm very confused as to why are we doing disjunctions here.
I think this corresponds to RangeSet being a union of Ranges.


https://reviews.llvm.org/D45517



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


[PATCH] D37023: [analyzer] Fix bugreporter::getDerefExpr() again.

2017-09-25 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 116542.
NoQ added a comment.

@dcoughlin: You're right, my reasoning and understanding was not correct, and 
your explanation is much more clear. My code still makes sense to me though, so 
i updated the comments to match. And moved the unusual logic for the 
lvalue-to-rvalue cast unwrap to the bottom of the function.

Essentially, `getDerefExpr()` tries its best to explain where the null pointer 
comes from, on the syntactic level, by unpacking expressions that wrap the 
expression from which the pointer "originates", where "originates" is 
understood in a vague manner defined only by what the callers of this utility 
function expect to see. This leaves us with a few kinds of expressions that we 
need to unwrap, and we shouldn't touch other expressions, leaving 
pattern-matching over them to the caller.

There are other facilities that work on non-syntactic level, like your example 
where we might jump from function expression to return statement within the 
callee if the callee was inlined, or how `getNilReceiver()` function unwraps 
`ObjCMessageExpr` to the `self` pointer *iff* it `self` was `nil` during 
symbolic execution (which implies that the whole message expression is `nil`).

So that's right - lvalue-to-rvalue casts are not "the whole point", but merely 
an example of an expression kind that we do not have a right to unwrap. In 
fact, the callers still expect us to unwrap it once, but immediately stop 
afterwards. This inconsistency should probably be fixed, but `getDerefExpr` is 
used by checkers, and current code in checkers seems clean and concise enough.

I had a look at other cast kinds in `OperationKinds.def`, and all of them 
seemed as if they're worth unwrapping, apart from, of course, 
`CK_LValueToRValue`.

@xazax.hun: So, i guess, if the cast is earlier than we'd expect, then we'd 
just fail to unwrap something. So we won't find where the pointer comes from, 
and give up prematurely on our pattern-matching, while looking at roughly the 
same expression/value, plus-minus offsets. It sounds better than the case when 
the cast is //later// than we'd expect, where we may accidentally unwrap wrong 
stuff and start tracking a completely unrelated value, so i hope it shouldn't 
be a problem. Thanks for sharing the concern - the AST is huge and very few 
people possess really deep understanding of it, so any curious findings about 
it are really nice to share.


https://reviews.llvm.org/D37023

Files:
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  test/Analysis/null-deref-path-notes.m

Index: test/Analysis/null-deref-path-notes.m
===
--- test/Analysis/null-deref-path-notes.m
+++ test/Analysis/null-deref-path-notes.m
@@ -50,6 +50,23 @@
   *p = 1; // expected-warning{{Dereference of null pointer}} expected-note{{Dereference of null pointer}}
 }
 
+@interface WithArrayPtr
+- (void) useArray;
+@end
+
+@implementation WithArrayPtr {
+@public int *p;
+}
+- (void)useArray {
+  p[1] = 2; // expected-warning{{Array access (via ivar 'p') results in a null pointer dereference}}
+// expected-note@-1{{Array access (via ivar 'p') results in a null pointer dereference}}
+}
+@end
+
+void testWithArrayPtr(WithArrayPtr *w) {
+  w->p = 0; // expected-note{{Null pointer value stored to 'p'}}
+  [w useArray]; // expected-note{{Calling 'useArray'}}
+}
 
 // CHECK:  diagnostics
 // CHECK-NEXT:  
@@ -801,4 +818,227 @@
 // CHECK-NEXT:file0
 // CHECK-NEXT:   
 // CHECK-NEXT:   
+// CHECK-NEXT:   
+// CHECK-NEXT:path
+// CHECK-NEXT:
+// CHECK-NEXT: 
+// CHECK-NEXT:  kindevent
+// CHECK-NEXT:  location
+// CHECK-NEXT:  
+// CHECK-NEXT:   line67
+// CHECK-NEXT:   col3
+// CHECK-NEXT:   file0
+// CHECK-NEXT:  
+// CHECK-NEXT:  ranges
+// CHECK-NEXT:  
+// CHECK-NEXT:
+// CHECK-NEXT: 
+// CHECK-NEXT:  line67
+// CHECK-NEXT:  col3
+// CHECK-NEXT:  file0
+// CHECK-NEXT: 
+// CHECK-NEXT: 
+// CHECK-NEXT:  line67
+// CHECK-NEXT:  col10
+// CHECK-NEXT:  file0
+// CHECK-NEXT: 
+// CHECK-NEXT:
+// CHECK-NEXT:  
+// CHECK-NEXT:  depth0
+// CHECK-NEXT:  extended_message
+// CHECK-NEXT:  Null pointer value stored to 'p'
+// CHECK-NEXT:  message
+// CHECK-NEXT:  Null pointer value stored to 'p'
+// CHECK-NEXT: 
+// CHECK-NEXT: 
+// CHECK-NEXT:  kindcontrol
+// CHECK-NEXT:  edges
+// CHECK-NEXT:   
+// CHECK-NEXT:
+// CHECK-NEXT: start
+// CHECK-NEXT:  
+// CHECK-NEXT:   
+// CHECK-NEXT:line67
+// CHECK-NEXT:col3
+// CHECK-NEXT:file0
+// CHECK-NEXT:   
+// CHECK-NEXT:   
+// CHECK-NEXT:line67
+// CHECK-NEXT:col3
+// CHECK-NEXT:file0
+// CHECK-NEXT:   
+// CHECK-NEXT:  
+// CHECK-NEXT: end
+// 

[PATCH] D37023: [analyzer] Fix bugreporter::getDerefExpr() again.

2017-09-26 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 116673.
NoQ added a comment.

Add no-crash test cases from https://bugs.llvm.org/show_bug.cgi?id=34373 and 
https://bugs.llvm.org/show_bug.cgi?id=34731 .


https://reviews.llvm.org/D37023

Files:
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  test/Analysis/null-deref-path-notes.c
  test/Analysis/null-deref-path-notes.cpp
  test/Analysis/null-deref-path-notes.m

Index: test/Analysis/null-deref-path-notes.m
===
--- test/Analysis/null-deref-path-notes.m
+++ test/Analysis/null-deref-path-notes.m
@@ -50,6 +50,23 @@
   *p = 1; // expected-warning{{Dereference of null pointer}} expected-note{{Dereference of null pointer}}
 }
 
+@interface WithArrayPtr
+- (void) useArray;
+@end
+
+@implementation WithArrayPtr {
+@public int *p;
+}
+- (void)useArray {
+  p[1] = 2; // expected-warning{{Array access (via ivar 'p') results in a null pointer dereference}}
+// expected-note@-1{{Array access (via ivar 'p') results in a null pointer dereference}}
+}
+@end
+
+void testWithArrayPtr(WithArrayPtr *w) {
+  w->p = 0; // expected-note{{Null pointer value stored to 'p'}}
+  [w useArray]; // expected-note{{Calling 'useArray'}}
+}
 
 // CHECK:  diagnostics
 // CHECK-NEXT:  
@@ -801,4 +818,227 @@
 // CHECK-NEXT:file0
 // CHECK-NEXT:   
 // CHECK-NEXT:   
+// CHECK-NEXT:   
+// CHECK-NEXT:path
+// CHECK-NEXT:
+// CHECK-NEXT: 
+// CHECK-NEXT:  kindevent
+// CHECK-NEXT:  location
+// CHECK-NEXT:  
+// CHECK-NEXT:   line67
+// CHECK-NEXT:   col3
+// CHECK-NEXT:   file0
+// CHECK-NEXT:  
+// CHECK-NEXT:  ranges
+// CHECK-NEXT:  
+// CHECK-NEXT:
+// CHECK-NEXT: 
+// CHECK-NEXT:  line67
+// CHECK-NEXT:  col3
+// CHECK-NEXT:  file0
+// CHECK-NEXT: 
+// CHECK-NEXT: 
+// CHECK-NEXT:  line67
+// CHECK-NEXT:  col10
+// CHECK-NEXT:  file0
+// CHECK-NEXT: 
+// CHECK-NEXT:
+// CHECK-NEXT:  
+// CHECK-NEXT:  depth0
+// CHECK-NEXT:  extended_message
+// CHECK-NEXT:  Null pointer value stored to 'p'
+// CHECK-NEXT:  message
+// CHECK-NEXT:  Null pointer value stored to 'p'
+// CHECK-NEXT: 
+// CHECK-NEXT: 
+// CHECK-NEXT:  kindcontrol
+// CHECK-NEXT:  edges
+// CHECK-NEXT:   
+// CHECK-NEXT:
+// CHECK-NEXT: start
+// CHECK-NEXT:  
+// CHECK-NEXT:   
+// CHECK-NEXT:line67
+// CHECK-NEXT:col3
+// CHECK-NEXT:file0
+// CHECK-NEXT:   
+// CHECK-NEXT:   
+// CHECK-NEXT:line67
+// CHECK-NEXT:col3
+// CHECK-NEXT:file0
+// CHECK-NEXT:   
+// CHECK-NEXT:  
+// CHECK-NEXT: end
+// CHECK-NEXT:  
+// CHECK-NEXT:   
+// CHECK-NEXT:line68
+// CHECK-NEXT:col3
+// CHECK-NEXT:file0
+// CHECK-NEXT:   
+// CHECK-NEXT:   
+// CHECK-NEXT:line68
+// CHECK-NEXT:col3
+// CHECK-NEXT:file0
+// CHECK-NEXT:   
+// CHECK-NEXT:  
+// CHECK-NEXT:
+// CHECK-NEXT:   
+// CHECK-NEXT: 
+// CHECK-NEXT: 
+// CHECK-NEXT:  kindevent
+// CHECK-NEXT:  location
+// CHECK-NEXT:  
+// CHECK-NEXT:   line68
+// CHECK-NEXT:   col3
+// CHECK-NEXT:   file0
+// CHECK-NEXT:  
+// CHECK-NEXT:  ranges
+// CHECK-NEXT:  
+// CHECK-NEXT:
+// CHECK-NEXT: 
+// CHECK-NEXT:  line68
+// CHECK-NEXT:  col3
+// CHECK-NEXT:  file0
+// CHECK-NEXT: 
+// CHECK-NEXT: 
+// CHECK-NEXT:  line68
+// CHECK-NEXT:  col14
+// CHECK-NEXT:  file0
+// CHECK-NEXT: 
+// CHECK-NEXT:
+// CHECK-NEXT:  
+// CHECK-NEXT:  depth0
+// CHECK-NEXT:  extended_message
+// CHECK-NEXT:  Calling 'useArray'
+// CHECK-NEXT:  message
+// CHECK-NEXT:  Calling 'useArray'
+// CHECK-NEXT: 
+// CHECK-NEXT: 
+// CHECK-NEXT:  kindevent
+// CHECK-NEXT:  location
+// CHECK-NEXT:  
+// CHECK-NEXT:   line60
+// CHECK-NEXT:   col1
+// CHECK-NEXT:   file0
+// CHECK-NEXT:  
+// CHECK-NEXT:  depth1
+// CHECK-NEXT:  extended_message
+// CHECK-NEXT:  Entered call from 'testWithArrayPtr'
+// CHECK-NEXT:  message
+// CHECK-NEXT:  Entered call from 'testWithArrayPtr'
+// CHECK-NEXT: 
+// CHECK-NEXT: 
+// CHECK-NEXT:  kindcontrol
+// CHECK-NEXT:  edges
+// CHECK-NEXT:   
+// CHECK-NEXT:
+// CHECK-NEXT: start
+// CHECK-NEXT:  
+// CHECK-NEXT:   
+// CHECK-NEXT:line60
+// CHECK-NEXT:col1
+// CHECK-NEXT:file0
+// CHECK-NEXT:   
+// CHECK-NEXT:   
+// CHECK-NEXT:line60
+// CHECK-NEXT:col1
+// CHECK-NEXT:file0
+// CHECK-NEXT:   
+// CHECK-NEX

[PATCH] D38358: [analyzer] Fix autodetection of getSVal()'s type argument.

2017-09-28 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.

In `ProgramState::getSVal(Loc, QualType)`, the `QualType` parameter, which 
represents the type of the expected value, is optional. If it is not supplied, 
it is auto-detected by looking at the memory region in `Loc`. For typed-value 
regions such autodetection is trivial. For other kinds of regions (most 
importantly, for symbolic regions) it apparently never worked correctly: it 
detected the location type (pointer type), not the value type in this location 
(pointee type).

Our tests contain numerous cases when such autodetection was working 
incorrectly, but for existing tests it never mattered. There are three notable 
places where the type was regularly auto-detected incorrectly:

1. `ExprEngine::performTrivialCopy()`. Trivial copy from symbolic references 
never worked - test case attached.
2. `CallAndMessageChecker::uninitRefOrPointer()`. Here the code only cares if 
the value is `Undefined` or not, so autodetected type didn't matter.
3. `GTestChecker::modelAssertionResultBoolConstructor()`. This is how the issue 
was found in https://bugs.llvm.org/show_bug.cgi?id=34305 - test case attached.

I added a few more sanity checks - we'd now also fail with an assertion if we 
are unable to autodetect the pointer type, warning the author of the checker to 
pass the type explicitly.

It is sometimes indeed handy to put a void-pointer-typed `Loc` into 
`getSVal(Loc)`, as demonstrated in the `exercise-ps.c`'s `f2()` test through 
`CallAndMessageChecker` (case '2.' above). I handled this on the API side in 
order to simplify checker API, implicitly turning `void` into `char`, though i 
don't mind modifying `CallAndMessageChecker` to pass `CharTy` explicitly 
instead.


https://reviews.llvm.org/D38358

Files:
  lib/StaticAnalyzer/Core/RegionStore.cpp
  test/Analysis/ctor.mm
  test/Analysis/gtest.cpp


Index: test/Analysis/gtest.cpp
===
--- test/Analysis/gtest.cpp
+++ test/Analysis/gtest.cpp
@@ -151,3 +151,9 @@
   ASSERT_TRUE(false);
   clang_analyzer_warnIfReached(); // no-warning
 }
+
+void testAssertSymbolic(const bool &b) {
+  ASSERT_TRUE(b);
+
+  clang_analyzer_eval(b); // expected-warning{{UNKNOWN}}
+}
Index: test/Analysis/ctor.mm
===
--- test/Analysis/ctor.mm
+++ test/Analysis/ctor.mm
@@ -199,7 +199,7 @@
 Inner p;
   };
 
-  void testPOD() {
+  void testPOD(const POD &pp) {
 POD p;
 p.x = 1;
 POD p2 = p; // no-warning
@@ -210,6 +210,15 @@
 // Use rvalues as well.
 clang_analyzer_eval(POD(p3).x == 1); // expected-warning{{TRUE}}
 
+// Copy from symbolic references correctly.
+POD p4 = pp;
+// Make sure that p4.x contains a symbol after copy.
+if (p4.x > 0)
+  clang_analyzer_eval(p4.x > 0); // expected-warning{{TRUE}}
+// FIXME: Element region gets in the way, so these aren't the same symbols
+// as they should be.
+clang_analyzer_eval(pp.x == p4.x); // expected-warning{{UNKNOWN}}
+
 PODWrapper w;
 w.p.y = 1;
 PODWrapper w2 = w; // no-warning
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1393,17 +1393,20 @@
 return UnknownVal();
   }
 
-  if (isa(MR) ||
-  isa(MR) ||
-  isa(MR)) {
+  if (!isa(MR)) {
 if (T.isNull()) {
-  if (const TypedRegion *TR = dyn_cast(MR))
-T = TR->getLocationType();
-  else {
-const SymbolicRegion *SR = cast(MR);
-T = SR->getSymbol()->getType();
+  if (const TypedRegion *TR = dyn_cast(MR)) {
+T = TR->getLocationType()->getPointeeType();
+  } else if (const SymbolicRegion *SR = dyn_cast(MR)) {
+T = SR->getSymbol()->getType()->getPointeeType();
+if (T->isVoidType()) {
+  // When trying to dereference a void pointer, read the first byte.
+  T = Ctx.CharTy;
+}
   }
 }
+assert(!T.isNull() && "Unable to auto-detect binding type!");
+assert(!T->isVoidType() && "Attempted to retrieve a void value!");
 MR = GetElementZeroRegion(cast(MR), T);
   }
 


Index: test/Analysis/gtest.cpp
===
--- test/Analysis/gtest.cpp
+++ test/Analysis/gtest.cpp
@@ -151,3 +151,9 @@
   ASSERT_TRUE(false);
   clang_analyzer_warnIfReached(); // no-warning
 }
+
+void testAssertSymbolic(const bool &b) {
+  ASSERT_TRUE(b);
+
+  clang_analyzer_eval(b); // expected-warning{{UNKNOWN}}
+}
Index: test/Analysis/ctor.mm
===
--- test/Analysis/ctor.mm
+++ test/Analysis/ctor.mm
@@ -199,7 +199,7 @@
 Inner p;
   };
 
-  void testPOD() {
+  void testPOD(const POD &pp) {
 POD p;
 p.x = 1;
 POD p2 = p; // no-warning
@@ -210,6 +210,15 @@
 // Use rvalues as well.
 clang_analyzer_eval(POD(

[PATCH] D38358: [analyzer] Fix autodetection of getSVal()'s type argument.

2017-09-28 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 116986.
NoQ added a comment.

Add a forgotten comment.


https://reviews.llvm.org/D38358

Files:
  lib/StaticAnalyzer/Core/RegionStore.cpp
  test/Analysis/ctor.mm
  test/Analysis/gtest.cpp


Index: test/Analysis/gtest.cpp
===
--- test/Analysis/gtest.cpp
+++ test/Analysis/gtest.cpp
@@ -151,3 +151,10 @@
   ASSERT_TRUE(false);
   clang_analyzer_warnIfReached(); // no-warning
 }
+
+void testAssertSymbolic(const bool &b) {
+  ASSERT_TRUE(b);
+
+  // FIXME: Our solver doesn't handle this well yet.
+  clang_analyzer_eval(b); // expected-warning{{UNKNOWN}}
+}
Index: test/Analysis/ctor.mm
===
--- test/Analysis/ctor.mm
+++ test/Analysis/ctor.mm
@@ -199,7 +199,7 @@
 Inner p;
   };
 
-  void testPOD() {
+  void testPOD(const POD &pp) {
 POD p;
 p.x = 1;
 POD p2 = p; // no-warning
@@ -210,6 +210,15 @@
 // Use rvalues as well.
 clang_analyzer_eval(POD(p3).x == 1); // expected-warning{{TRUE}}
 
+// Copy from symbolic references correctly.
+POD p4 = pp;
+// Make sure that p4.x contains a symbol after copy.
+if (p4.x > 0)
+  clang_analyzer_eval(p4.x > 0); // expected-warning{{TRUE}}
+// FIXME: Element region gets in the way, so these aren't the same symbols
+// as they should be.
+clang_analyzer_eval(pp.x == p4.x); // expected-warning{{UNKNOWN}}
+
 PODWrapper w;
 w.p.y = 1;
 PODWrapper w2 = w; // no-warning
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1393,17 +1393,20 @@
 return UnknownVal();
   }
 
-  if (isa(MR) ||
-  isa(MR) ||
-  isa(MR)) {
+  if (!isa(MR)) {
 if (T.isNull()) {
-  if (const TypedRegion *TR = dyn_cast(MR))
-T = TR->getLocationType();
-  else {
-const SymbolicRegion *SR = cast(MR);
-T = SR->getSymbol()->getType();
+  if (const TypedRegion *TR = dyn_cast(MR)) {
+T = TR->getLocationType()->getPointeeType();
+  } else if (const SymbolicRegion *SR = dyn_cast(MR)) {
+T = SR->getSymbol()->getType()->getPointeeType();
+if (T->isVoidType()) {
+  // When trying to dereference a void pointer, read the first byte.
+  T = Ctx.CharTy;
+}
   }
 }
+assert(!T.isNull() && "Unable to auto-detect binding type!");
+assert(!T->isVoidType() && "Attempted to retrieve a void value!");
 MR = GetElementZeroRegion(cast(MR), T);
   }
 


Index: test/Analysis/gtest.cpp
===
--- test/Analysis/gtest.cpp
+++ test/Analysis/gtest.cpp
@@ -151,3 +151,10 @@
   ASSERT_TRUE(false);
   clang_analyzer_warnIfReached(); // no-warning
 }
+
+void testAssertSymbolic(const bool &b) {
+  ASSERT_TRUE(b);
+
+  // FIXME: Our solver doesn't handle this well yet.
+  clang_analyzer_eval(b); // expected-warning{{UNKNOWN}}
+}
Index: test/Analysis/ctor.mm
===
--- test/Analysis/ctor.mm
+++ test/Analysis/ctor.mm
@@ -199,7 +199,7 @@
 Inner p;
   };
 
-  void testPOD() {
+  void testPOD(const POD &pp) {
 POD p;
 p.x = 1;
 POD p2 = p; // no-warning
@@ -210,6 +210,15 @@
 // Use rvalues as well.
 clang_analyzer_eval(POD(p3).x == 1); // expected-warning{{TRUE}}
 
+// Copy from symbolic references correctly.
+POD p4 = pp;
+// Make sure that p4.x contains a symbol after copy.
+if (p4.x > 0)
+  clang_analyzer_eval(p4.x > 0); // expected-warning{{TRUE}}
+// FIXME: Element region gets in the way, so these aren't the same symbols
+// as they should be.
+clang_analyzer_eval(pp.x == p4.x); // expected-warning{{UNKNOWN}}
+
 PODWrapper w;
 w.p.y = 1;
 PODWrapper w2 = w; // no-warning
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1393,17 +1393,20 @@
 return UnknownVal();
   }
 
-  if (isa(MR) ||
-  isa(MR) ||
-  isa(MR)) {
+  if (!isa(MR)) {
 if (T.isNull()) {
-  if (const TypedRegion *TR = dyn_cast(MR))
-T = TR->getLocationType();
-  else {
-const SymbolicRegion *SR = cast(MR);
-T = SR->getSymbol()->getType();
+  if (const TypedRegion *TR = dyn_cast(MR)) {
+T = TR->getLocationType()->getPointeeType();
+  } else if (const SymbolicRegion *SR = dyn_cast(MR)) {
+T = SR->getSymbol()->getType()->getPointeeType();
+if (T->isVoidType()) {
+  // When trying to dereference a void pointer, read the first byte.
+  T = Ctx.CharTy;
+}
   }
 }
+assert(!T.isNull() && "Unable to auto-detect binding type!");
+assert(!T->is

[PATCH] D38358: [analyzer] Fix autodetection of getSVal()'s type argument.

2017-09-28 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 116992.
NoQ added a subscriber: alexfh.
NoQ added a comment.

Add @alexfh's small reproducer test case. It was so small i never noticed it 
until now!


https://reviews.llvm.org/D38358

Files:
  lib/StaticAnalyzer/Core/RegionStore.cpp
  test/Analysis/ctor.mm
  test/Analysis/gtest.cpp


Index: test/Analysis/gtest.cpp
===
--- test/Analysis/gtest.cpp
+++ test/Analysis/gtest.cpp
@@ -151,3 +151,17 @@
   ASSERT_TRUE(false);
   clang_analyzer_warnIfReached(); // no-warning
 }
+
+void testAssertSymbolicPtr(const bool *b) {
+  ASSERT_TRUE(*b); // no-crash
+
+  // FIXME: Our solver doesn't handle this well yet.
+  clang_analyzer_eval(*b); // expected-warning{{UNKNOWN}}
+}
+
+void testAssertSymbolicRef(const bool &b) {
+  ASSERT_TRUE(b); // no-crash
+
+  // FIXME: Our solver doesn't handle this well yet.
+  clang_analyzer_eval(b); // expected-warning{{UNKNOWN}}
+}
Index: test/Analysis/ctor.mm
===
--- test/Analysis/ctor.mm
+++ test/Analysis/ctor.mm
@@ -199,7 +199,7 @@
 Inner p;
   };
 
-  void testPOD() {
+  void testPOD(const POD &pp) {
 POD p;
 p.x = 1;
 POD p2 = p; // no-warning
@@ -210,6 +210,15 @@
 // Use rvalues as well.
 clang_analyzer_eval(POD(p3).x == 1); // expected-warning{{TRUE}}
 
+// Copy from symbolic references correctly.
+POD p4 = pp;
+// Make sure that p4.x contains a symbol after copy.
+if (p4.x > 0)
+  clang_analyzer_eval(p4.x > 0); // expected-warning{{TRUE}}
+// FIXME: Element region gets in the way, so these aren't the same symbols
+// as they should be.
+clang_analyzer_eval(pp.x == p4.x); // expected-warning{{UNKNOWN}}
+
 PODWrapper w;
 w.p.y = 1;
 PODWrapper w2 = w; // no-warning
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1393,17 +1393,20 @@
 return UnknownVal();
   }
 
-  if (isa(MR) ||
-  isa(MR) ||
-  isa(MR)) {
+  if (!isa(MR)) {
 if (T.isNull()) {
-  if (const TypedRegion *TR = dyn_cast(MR))
-T = TR->getLocationType();
-  else {
-const SymbolicRegion *SR = cast(MR);
-T = SR->getSymbol()->getType();
+  if (const TypedRegion *TR = dyn_cast(MR)) {
+T = TR->getLocationType()->getPointeeType();
+  } else if (const SymbolicRegion *SR = dyn_cast(MR)) {
+T = SR->getSymbol()->getType()->getPointeeType();
+if (T->isVoidType()) {
+  // When trying to dereference a void pointer, read the first byte.
+  T = Ctx.CharTy;
+}
   }
 }
+assert(!T.isNull() && "Unable to auto-detect binding type!");
+assert(!T->isVoidType() && "Attempted to retrieve a void value!");
 MR = GetElementZeroRegion(cast(MR), T);
   }
 


Index: test/Analysis/gtest.cpp
===
--- test/Analysis/gtest.cpp
+++ test/Analysis/gtest.cpp
@@ -151,3 +151,17 @@
   ASSERT_TRUE(false);
   clang_analyzer_warnIfReached(); // no-warning
 }
+
+void testAssertSymbolicPtr(const bool *b) {
+  ASSERT_TRUE(*b); // no-crash
+
+  // FIXME: Our solver doesn't handle this well yet.
+  clang_analyzer_eval(*b); // expected-warning{{UNKNOWN}}
+}
+
+void testAssertSymbolicRef(const bool &b) {
+  ASSERT_TRUE(b); // no-crash
+
+  // FIXME: Our solver doesn't handle this well yet.
+  clang_analyzer_eval(b); // expected-warning{{UNKNOWN}}
+}
Index: test/Analysis/ctor.mm
===
--- test/Analysis/ctor.mm
+++ test/Analysis/ctor.mm
@@ -199,7 +199,7 @@
 Inner p;
   };
 
-  void testPOD() {
+  void testPOD(const POD &pp) {
 POD p;
 p.x = 1;
 POD p2 = p; // no-warning
@@ -210,6 +210,15 @@
 // Use rvalues as well.
 clang_analyzer_eval(POD(p3).x == 1); // expected-warning{{TRUE}}
 
+// Copy from symbolic references correctly.
+POD p4 = pp;
+// Make sure that p4.x contains a symbol after copy.
+if (p4.x > 0)
+  clang_analyzer_eval(p4.x > 0); // expected-warning{{TRUE}}
+// FIXME: Element region gets in the way, so these aren't the same symbols
+// as they should be.
+clang_analyzer_eval(pp.x == p4.x); // expected-warning{{UNKNOWN}}
+
 PODWrapper w;
 w.p.y = 1;
 PODWrapper w2 = w; // no-warning
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1393,17 +1393,20 @@
 return UnknownVal();
   }
 
-  if (isa(MR) ||
-  isa(MR) ||
-  isa(MR)) {
+  if (!isa(MR)) {
 if (T.isNull()) {
-  if (const TypedRegion *TR = dyn_cast(MR))
-T = TR->getLocationType();
-  else {
-const SymbolicRegion *SR = cast(

[PATCH] D35216: [analyzer] Escape symbols when creating std::initializer_list.

2017-10-02 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:1127
+// only consist of ObjC objects, and escapes of ObjC objects
+// aren't so important (eg., retain count checker ignores them).
+if (isa(Ex) ||

dcoughlin wrote:
> Note that we do have other ObjC checkers that rely on escaping of ObjC 
> objects, such as the ObjCLoopChecker and ObjCDeallocChecker. I think having 
> the TODO is great, but I'd like you to remove the the bit about "escapes of 
> ObjC objects aren't so important".
Hmm, should have double-checked. Will fix.



Comment at: test/Analysis/objc-boxing.m:66
+  BoxableStruct bs;
+  bs.str = strdup("dynamic string"); // The duped string shall be owned by val.
+  NSValue *val = @(bs); // no-warning

dcoughlin wrote:
> In this case the duped string is not owned by `val`. NSValue doesn't take 
> ownership of the string, so this *will* leak and we should warn about it.
I mean, the pointer to the raw string is stored inside the `NSValue`, and can 
be used or freed from there. The caller can free this string by looking into 
the `val`, even though `val` itself won't release the pointer (i guess i messed 
up the comment again). From MallocChecker's perspective, this is an escape and 
no-warning. If we free the string in this function, it'd most likely cause 
use-after-free in the caller.

I tested that the string is indeed not strduped during boxing:

**`$ cat test.m`**
```
#import 

typedef struct __attribute__((objc_boxable)) {
  const char *str;
} BoxableStruct;

int main() {
  BoxableStruct bs;
  bs.str = strdup("dynamic string");
  NSLog(@"%p\n", bs.str);

  NSValue *val = @(bs);

  BoxableStruct bs2;
  [val getValue:&bs2];
  NSLog(@"%p\n", bs2.str);

  return 0;
}
```
**`$ clang test.m -framework Foundation`**
**`$ ./a.out`**
```
2017-10-02 17:56:00.004 a.out[17933:1083757] 0x7ffd23407380
2017-10-02 17:56:00.004 a.out[17933:1083757] 0x7ffd23407380
```
So it's possible to retrieve the exact same pointer from the boxed value. So if 
`val` is returned to the caller, like in the test, it shouldn't be freed.

If the `NSValue` itself dies and never escapes, then of course it's a leak, but 
in order to see that we'd need to model contents of `NSValue`.


https://reviews.llvm.org/D35216



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


[PATCH] D38358: [analyzer] Fix autodetection of getSVal()'s type argument.

2017-10-02 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 117360.
NoQ added a comment.

Yeah, nice catch. So we need to either always tell the checkers to specify 
their `CharTy` when they are dealing with void pointers, or to do our 
substitution consistently, not only for `SymbolicRegion` but also for 
`AllocaRegion` (did that in this diff).


https://reviews.llvm.org/D38358

Files:
  lib/StaticAnalyzer/Core/RegionStore.cpp
  test/Analysis/ctor.mm
  test/Analysis/exercise-ps.c
  test/Analysis/gtest.cpp


Index: test/Analysis/gtest.cpp
===
--- test/Analysis/gtest.cpp
+++ test/Analysis/gtest.cpp
@@ -151,3 +151,17 @@
   ASSERT_TRUE(false);
   clang_analyzer_warnIfReached(); // no-warning
 }
+
+void testAssertSymbolicPtr(const bool *b) {
+  ASSERT_TRUE(*b); // no-crash
+
+  // FIXME: Our solver doesn't handle this well yet.
+  clang_analyzer_eval(*b); // expected-warning{{UNKNOWN}}
+}
+
+void testAssertSymbolicRef(const bool &b) {
+  ASSERT_TRUE(b); // no-crash
+
+  // FIXME: Our solver doesn't handle this well yet.
+  clang_analyzer_eval(b); // expected-warning{{UNKNOWN}}
+}
Index: test/Analysis/exercise-ps.c
===
--- test/Analysis/exercise-ps.c
+++ test/Analysis/exercise-ps.c
@@ -21,3 +21,11 @@
   memcpy((&x[1]), (buf), 1); // expected-warning{{implicitly declaring library 
function 'memcpy' with type 'void *(void *, const void *}} \
   // expected-note{{include the header  or explicitly provide a 
declaration for 'memcpy'}}
 }
+
+// AllocaRegion is untyped. Void pointer isn't of much help either. Before
+// realizing that the value is undefined, we need to somehow figure out
+// what type of value do we expect.
+void f3(void *dest) {
+  void *src = __builtin_alloca(5);
+  memcpy(dest, src, 1); // expected-warning{{2nd function call argument is a 
pointer to uninitialized value}}
+}
Index: test/Analysis/ctor.mm
===
--- test/Analysis/ctor.mm
+++ test/Analysis/ctor.mm
@@ -199,7 +199,7 @@
 Inner p;
   };
 
-  void testPOD() {
+  void testPOD(const POD &pp) {
 POD p;
 p.x = 1;
 POD p2 = p; // no-warning
@@ -210,6 +210,15 @@
 // Use rvalues as well.
 clang_analyzer_eval(POD(p3).x == 1); // expected-warning{{TRUE}}
 
+// Copy from symbolic references correctly.
+POD p4 = pp;
+// Make sure that p4.x contains a symbol after copy.
+if (p4.x > 0)
+  clang_analyzer_eval(p4.x > 0); // expected-warning{{TRUE}}
+// FIXME: Element region gets in the way, so these aren't the same symbols
+// as they should be.
+clang_analyzer_eval(pp.x == p4.x); // expected-warning{{UNKNOWN}}
+
 PODWrapper w;
 w.p.y = 1;
 PODWrapper w2 = w; // no-warning
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1393,16 +1393,19 @@
 return UnknownVal();
   }
 
-  if (isa(MR) ||
-  isa(MR) ||
-  isa(MR)) {
+  if (!isa(MR)) {
 if (T.isNull()) {
   if (const TypedRegion *TR = dyn_cast(MR))
-T = TR->getLocationType();
-  else {
-const SymbolicRegion *SR = cast(MR);
-T = SR->getSymbol()->getType();
-  }
+T = TR->getLocationType()->getPointeeType();
+  else if (const SymbolicRegion *SR = dyn_cast(MR))
+T = SR->getSymbol()->getType()->getPointeeType();
+  else if (isa(MR))
+T = Ctx.VoidTy;
+}
+assert(!T.isNull() && "Unable to auto-detect binding type!");
+if (T->isVoidType()) {
+  // When trying to dereference a void pointer, read the first byte.
+  T = Ctx.CharTy;
 }
 MR = GetElementZeroRegion(cast(MR), T);
   }


Index: test/Analysis/gtest.cpp
===
--- test/Analysis/gtest.cpp
+++ test/Analysis/gtest.cpp
@@ -151,3 +151,17 @@
   ASSERT_TRUE(false);
   clang_analyzer_warnIfReached(); // no-warning
 }
+
+void testAssertSymbolicPtr(const bool *b) {
+  ASSERT_TRUE(*b); // no-crash
+
+  // FIXME: Our solver doesn't handle this well yet.
+  clang_analyzer_eval(*b); // expected-warning{{UNKNOWN}}
+}
+
+void testAssertSymbolicRef(const bool &b) {
+  ASSERT_TRUE(b); // no-crash
+
+  // FIXME: Our solver doesn't handle this well yet.
+  clang_analyzer_eval(b); // expected-warning{{UNKNOWN}}
+}
Index: test/Analysis/exercise-ps.c
===
--- test/Analysis/exercise-ps.c
+++ test/Analysis/exercise-ps.c
@@ -21,3 +21,11 @@
   memcpy((&x[1]), (buf), 1); // expected-warning{{implicitly declaring library function 'memcpy' with type 'void *(void *, const void *}} \
   // expected-note{{include the header  or explicitly provide a declaration for 'memcpy'}}
 }
+
+// AllocaRegion is untyped. Void pointer isn't of much help either. Before
+// realizing that the v

[PATCH] D38358: [analyzer] Fix autodetection of getSVal()'s type argument.

2017-10-04 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

Whoops forgot to submit inline comments.




Comment at: lib/StaticAnalyzer/Core/RegionStore.cpp:1404
+  // When trying to dereference a void pointer, read the first byte.
+  T = Ctx.CharTy;
+}

dcoughlin wrote:
> Nit: It seems a bit odd to read the first byte here since (unless I'm 
> misunderstanding) this would never be triggered by actual C semantics, only 
> by a checker. Did you consider just returning UnknownVal() in this case?
`UnknownVal` seems to be quite similar to assertion failure: in both cases we'd 
have to force the checker to specify `CharTy` explicitly. But assertions are 
better because the author of the checker would instantly know that he needs to 
specify the type, instead of never noticing the problem, or spending a lot of 
time in the debugger trying to understand why he gets an `UnknownVal`.



Comment at: lib/StaticAnalyzer/Core/RegionStore.cpp:1408
 }
+assert(!T.isNull() && "Unable to auto-detect binding type!");
+assert(!T->isVoidType() && "Attempted to retrieve a void value!");

dcoughlin wrote:
> I think you missed handling the AllocaRegion case from the old version in 
> your new version. This means the assert will fire on the following when 
> core.alpha is enabled:
> ```
> void foo(void *dest) {
>   void *src = __builtin_alloca(5);
>   memcpy(dest, src, 1);
> }
> ```
Nice one!

Always been that way though. In the old code, if `AllocaRegion` is supplied and 
no type is explicitly specified, `cast(MR)` would fail. Also 
`ElementRegion` is created in both old and new code if the type was specified.


https://reviews.llvm.org/D38358



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


[PATCH] D38358: [analyzer] Fix autodetection of getSVal()'s type argument.

2017-10-04 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/RegionStore.cpp:1404
+  // When trying to dereference a void pointer, read the first byte.
+  T = Ctx.CharTy;
+}

dcoughlin wrote:
> NoQ wrote:
> > dcoughlin wrote:
> > > Nit: It seems a bit odd to read the first byte here since (unless I'm 
> > > misunderstanding) this would never be triggered by actual C semantics, 
> > > only by a checker. Did you consider just returning UnknownVal() in this 
> > > case?
> > `UnknownVal` seems to be quite similar to assertion failure: in both cases 
> > we'd have to force the checker to specify `CharTy` explicitly. But 
> > assertions are better because the author of the checker would instantly 
> > know that he needs to specify the type, instead of never noticing the 
> > problem, or spending a lot of time in the debugger trying to understand why 
> > he gets an `UnknownVal`.
>  I think having an assertion with a helpful message indicating how the 
> checker author could fix the problem would be great! But you're not 
> triggering an assertion failure here since you're changing T to be CharTy. 
> Instead, you're papering over the problem by making up a fake value that 
> doesn't correspond to the program semantics. If you're going to paper over 
> the the problem and return a value, I think it is far preferable to use 
> UnknownVal. This would signal "we don't know what is going here" rather than 
> just making up a value that is likely wrong.
> If you're going to paper over the the problem and return a value, I think it 
> is far preferable to use UnknownVal.

If we return an UnknownVal, the user would never figure out what's wrong by 
looking at the value, i.e. what limitation of the analyzer he's stepping on (in 
fact he isn't, he's just using the APIs incorrectly, while we know very well 
what's going on). Additionally, returning the first byte makes API simpler in 
cases the type actually doesn't matter (which is why it's not provided), unlike 
returning UnknownVal. This makes me think that returning UnknownVal is worse 
than both returning first byte and stepping on assertion. However, yeah, if we 
try to model an API that manipulates objects of well-defined types through void 
pointers, returning bytes might cause issues when the user doesn't realize he 
needs to specify his types, especially when RegionStore would often ignore the 
type and only take the offset, unless it has no bindings and needs to return 
region value or derived symbol (so the user may easily fail to test this case).

I guess i'd follow up with a patch to remove the defensive behavior and bring 
back the assertion and modify checkers to provide types when necessary, and 
later see if it's worth it to remove auto-detection completely.


Repository:
  rL LLVM

https://reviews.llvm.org/D38358



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


[PATCH] D35216: [analyzer] Escape symbols when creating std::initializer_list.

2017-10-04 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ marked 3 inline comments as done.
NoQ added a comment.

In https://reviews.llvm.org/D35216#886212, @rsmith wrote:

> This is precisely how the rest of the compiler handles 
> `CXXStdInitializerListExpr`


Wow. Cool. I'd see what I can do. Yeah, it seems that this is a great case for 
us to pattern-match the implementations as well (the problems are still there 
for other STL stuff).

Do you think this patch should be blocked in favor of complete modeling?
Or i can land that and follow up with complete modeling later; it'd be a 
larger, but not much to discuss indeed.
Or i can try always "inlining" initializer_list constructor and methods during 
analysis, if all methods are available in the header in all implementations.


https://reviews.llvm.org/D35216



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


[PATCH] D35216: [analyzer] Escape symbols when creating std::initializer_list.

2017-10-04 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 117699.
NoQ added a comment.
Herald added a subscriber: szepet.

Escape into array and dictionary literals, add relevant tests. Fix the null 
statement check.


https://reviews.llvm.org/D35216

Files:
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  test/Analysis/initializer.cpp
  test/Analysis/objc-boxing.m
  test/Analysis/objc-for.m

Index: test/Analysis/objc-for.m
===
--- test/Analysis/objc-for.m
+++ test/Analysis/objc-for.m
@@ -1,6 +1,7 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.Loops,debug.ExprInspection -verify %s
 
 void clang_analyzer_eval(int);
+void clang_analyzer_warnIfReached();
 
 #define nil ((id)0)
 
@@ -20,11 +21,13 @@
 @interface NSArray : NSObject 
 - (NSUInteger)count;
 - (NSEnumerator *)objectEnumerator;
++ (NSArray *)arrayWithObjects:(const id [])objects count:(NSUInteger)count;
 @end
 
 @interface NSDictionary : NSObject 
 - (NSUInteger)count;
 - (id)objectForKey:(id)key;
++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id /*  */ [])keys count:(NSUInteger)count;
 @end
 
 @interface NSDictionary (SomeCategory)
@@ -324,3 +327,19 @@
   for (id key in array)
 clang_analyzer_eval(0); // expected-warning{{FALSE}}
 }
+
+NSArray *globalArray;
+NSDictionary *globalDictionary;
+void boxedArrayEscape(NSMutableArray *array) {
+  if ([array count])
+return;
+  globalArray = @[array];
+  for (id key in array)
+clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+
+  if ([array count])
+return;
+  globalDictionary = @{ @"array" : array };
+  for (id key in array)
+clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
Index: test/Analysis/objc-boxing.m
===
--- test/Analysis/objc-boxing.m
+++ test/Analysis/objc-boxing.m
@@ -5,6 +5,16 @@
 typedef signed char BOOL;
 typedef long NSInteger;
 typedef unsigned long NSUInteger;
+
+@protocol NSObject
+@end
+@interface NSObject  {}
+@end
+@protocol NSCopying
+@end
+@protocol NSCoding
+@end
+
 @interface NSString @end
 @interface NSString (NSStringExtensionMethods)
 + (id)stringWithUTF8String:(const char *)nullTerminatedCString;
@@ -28,7 +38,15 @@
 + (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value ;
 @end
 
+@interface NSValue : NSObject 
+- (void)getValue:(void *)value;
++ (NSValue *)valueWithBytes:(const void *)value
+   objCType:(const char *)type;
+@end
 
+typedef typeof(sizeof(int)) size_t;
+extern void *malloc(size_t);
+extern void free(void *);
 extern char *strdup(const char *str);
 
 id constant_string() {
@@ -39,6 +57,23 @@
 return @(strdup("boxed dynamic string")); // expected-warning{{Potential memory leak}}
 }
 
+typedef struct __attribute__((objc_boxable)) {
+  const char *str;
+} BoxableStruct;
+
+id leak_within_boxed_struct() {
+  BoxableStruct bs;
+  bs.str = strdup("dynamic string"); // The duped string shall be owned by val.
+  NSValue *val = @(bs); // no-warning
+  return val;
+}
+
+id leak_of_boxed_struct() {
+  BoxableStruct *bs = malloc(sizeof(BoxableStruct)); // The pointer stored in bs isn't owned by val.
+  NSValue *val = @(*bs); // expected-warning{{Potential leak of memory pointed to by 'bs'}}
+  return val;
+}
+
 id const_char_pointer(int *x) {
   if (x)
 return @(3);
Index: test/Analysis/initializer.cpp
===
--- test/Analysis/initializer.cpp
+++ test/Analysis/initializer.cpp
@@ -1,7 +1,9 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-inlining=constructors -std=c++11 -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-inlining=constructors -std=c++11 -verify %s
 
 void clang_analyzer_eval(bool);
 
+#include "Inputs/system-header-simulator-cxx.h"
+
 class A {
   int x;
 public:
@@ -204,3 +206,17 @@
const char(&f)[2];
 };
 }
+
+namespace CXX_initializer_lists {
+struct C {
+  C(std::initializer_list list);
+};
+void foo() {
+  C empty{}; // no-crash
+
+  // Do not warn that 'x' leaks. It might have been deleted by
+  // the destructor of 'c'.
+  int *x = new int;
+  C c{x}; // no-warning
+}
+}
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -827,6 +827,21 @@
   }
 }
 
+namespace {
+class CollectReachableSymbolsCallback final : public SymbolVisitor {
+  InvalidatedSymbols Symbols;
+
+public:
+  explicit CollectReachableSymbolsCallback(ProgramStateRef State) {}
+  const InvalidatedSymbols &getSymbols() const { return Symbols; }
+
+  bool VisitSymbol(SymbolRef Sym) override {
+Symbols.insert(Sym);
+return true;
+  }
+};
+} // end anonymous namespace
+
 void ExprEngine::Visit(const Stmt *S, Explode

[PATCH] D35216: [analyzer] Escape symbols when creating std::initializer_list.

2017-10-04 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: test/Analysis/objc-for.m:328
   for (id key in array)
 clang_analyzer_eval(0); // expected-warning{{FALSE}}
 }

I guess these old tests should be replaced with `warnIfReached()`.


https://reviews.llvm.org/D35216



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


[PATCH] D35216: [analyzer] Escape symbols when creating std::initializer_list.

2017-10-04 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

In https://reviews.llvm.org/D35216#886212, @rsmith wrote:

> This is precisely how the rest of the compiler handles 
> `CXXStdInitializerListExpr`


Wow. Cool. I'd see what I can do. Yeah, it seems that this is a great case for 
us to pattern-match the implementations as well (the problems are still there 
for other STL stuff).

Do you think this patch should be blocked? Or i can land that and follow up 
with complete modeling later; it'd be larger.




Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:1132
+  for (auto Child : Ex->children()) {
+if (!Child)
+  continue;

dcoughlin wrote:
> Is this 'if' needed?
Not sure, wanted to be defensive. It seems that `CXXStdInitializerList` always 
contains a single child (`InitListExpr`) so it's irrelevant if the list is 
empty, while boxed expressions contain no children when empty, and hanging 
commas are ignored. Replaced with an assertion just in case.


https://reviews.llvm.org/D35216



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


  1   2   3   4   5   6   7   8   9   10   >