[analyzer] Fix yet-another-crash in body-farming std::call_once

Crash occurs when parameters to the callback and to std::call_once
mismatch, and C++ is supposed to auto-construct an argument.

Filed by Alexander Kornienko in
https://bugs.llvm.org/show_bug.cgi?id=36149

rdar://37034403

Differential Revision: https://reviews.llvm.org/D42777

llvm-svn: 324046
diff --git a/clang/test/Analysis/call_once.cpp b/clang/test/Analysis/call_once.cpp
index 445c616..dd4b2d4 100644
--- a/clang/test/Analysis/call_once.cpp
+++ b/clang/test/Analysis/call_once.cpp
@@ -9,9 +9,26 @@
 
 void clang_analyzer_eval(bool);
 
-// Faking std::std::call_once implementation.
+// Faking std::call_once implementation.
 namespace std {
 
+// Fake std::function implementation.
+template <typename>
+class function;
+class function_base {
+ public:
+  long field;
+};
+template <typename R, typename... P>
+class function<R(P...)> : function_base {
+ public:
+   R operator()(P...) const {
+
+     // Read from a super-class necessary to reproduce a crash.
+     bool a = field;
+   }
+};
+
 #ifndef EMULATE_LIBSTDCPP
 typedef struct once_flag_s {
   unsigned long __state_ = 0;
@@ -360,3 +377,29 @@
   clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
 #endif
 }
+
+int param_passed(int *x) {
+  return *x; // no-warning, as std::function is not working yet.
+}
+
+void callback_taking_func_ok(std::function<void(int*)> &innerCallback) {
+  innerCallback(nullptr);
+}
+
+// The provided callback expects an std::function, but instead a pointer
+// to a C++ function is provided.
+void callback_with_implicit_cast_ok() {
+  std::once_flag flag;
+  call_once(flag, callback_taking_func_ok, &param_passed);
+}
+
+void callback_taking_func(std::function<void()> &innerCallback) {
+  innerCallback();
+}
+
+// The provided callback expects an std::function, but instead a C function
+// name is provided, and C++ implicitly auto-constructs a pointer from it.
+void callback_with_implicit_cast() {
+  std::once_flag flag;
+  call_once(flag, callback_taking_func, callback_with_implicit_cast);
+}