[analyzer] Silence warnings coming from allocators used by std::basic_string.

This is similar to r194004: because we can't reason about the data structure
invariants of std::basic_string, the analyzer decides it's possible for an
allocator to be used to deallocate the string's inline storage. Just ignore
this by walking up the stack, skipping past methods in classes with
"allocator" in the name, and seeing if we reach std::basic_string that way.

PR17866

llvm-svn: 194764
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 2aa65fd..e1a92b3 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -1549,18 +1549,32 @@
           return 0;
         }
       }
+
       // The analyzer issues a false positive on
       //   std::basic_string<uint8_t> v; v.push_back(1);
+      // and
+      //   std::u16string s; s += u'a';
       // because we cannot reason about the internal invariants of the
       // datastructure.
-      if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
+      const LocationContext *LCtx = N->getLocationContext();
+      do {
+        const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
+        if (!MD)
+          break;
+
         const CXXRecordDecl *CD = MD->getParent();
         if (CD->getName() == "basic_string") {
           BR.markInvalid(getTag(), 0);
           return 0;
+        } else if (CD->getName().find("allocator") == StringRef::npos) {
+          // Only keep searching if the current method is in a class with the
+          // word "allocator" in its name, e.g. std::allocator or
+          // allocator_traits.
+          break;
         }
-      }
 
+        LCtx = LCtx->getParent();
+      } while (LCtx);
     }
   }
 
diff --git a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h
index ada94ca..9bb8ec4 100644
--- a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h
+++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h
@@ -118,7 +118,20 @@
   struct random_access_iterator_tag : public bidirectional_iterator_tag { };
 
   template <class _Tp>
-  class allocator {};
+  class allocator {
+  public:
+    void deallocate(void *p) {
+      ::delete p;
+    }
+  };
+
+  template <class _Alloc>
+  class allocator_traits {
+  public:
+    static void deallocate(void *p) {
+      _Alloc().deallocate(p);
+    }
+  };
 
   template <class _Tp, class _Alloc>
   class __list_imp
@@ -140,19 +153,28 @@
   };
 
   // basic_string
-  template<class _CharT>
+  template<class _CharT, class _Alloc = allocator<_CharT> >
   class __attribute__ ((__type_visibility__("default"))) basic_string {
+    _CharT localStorage[4];
+
+    typedef allocator_traits<_Alloc> __alloc_traits;
+
   public:
-    void push_back(int c);
+    void push_back(int c) {
+      // Fake error trigger.
+      // No warning is expected as we are suppressing warning comming
+      // out of std::basic_string.
+      int z = 0;
+      z = 5/z;
+    }
+
+    basic_string &operator +=(int c) {
+      // Fake deallocate stack-based storage.
+      // No warning is expected as we are suppressing warnings within
+      // allocators being used by std::basic_string.
+      __alloc_traits::deallocate(&localStorage);
+    }
   };
-  template <class _CharT>
-  void basic_string<_CharT>::push_back(int __c) {
-        // Fake error trigger.
-        // No warning is expected as we are suppressing warning comming
-        // out of std::basic_string.
-        int z = 0;
-        z = 5/z;
-  }
 }
 
 void* operator new(std::size_t, const std::nothrow_t&) throw();
diff --git a/clang/test/Analysis/inlining/stl.cpp b/clang/test/Analysis/inlining/stl.cpp
index fb73246..c153174 100644
--- a/clang/test/Analysis/inlining/stl.cpp
+++ b/clang/test/Analysis/inlining/stl.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-container-inlining=true -analyzer-config c++-stdlib-inlining=false -verify %s
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-container-inlining=true -analyzer-config c++-stdlib-inlining=true -DINLINE=1 -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,cplusplus.NewDelete,debug.ExprInspection -analyzer-config c++-container-inlining=true -analyzer-config c++-stdlib-inlining=false -std=c++11 -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,cplusplus.NewDelete,debug.ExprInspection -analyzer-config c++-container-inlining=true -analyzer-config c++-stdlib-inlining=true -std=c++11 -DINLINE=1 -verify %s
 
 #include "../Inputs/system-header-simulator-cxx.h"
 
@@ -38,3 +38,7 @@
   v.push_back(1); // no-warning
 }
 
+void testBasicStringSuppression_append() {
+  std::basic_string<char32_t> v;
+  v += 'c'; // no-warning
+}