[Analyzer] Synthesize function body for std::call_once

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

llvm-svn: 314571
diff --git a/clang/test/Analysis/call_once.cpp b/clang/test/Analysis/call_once.cpp
new file mode 100644
index 0000000..be0dc45
--- /dev/null
+++ b/clang/test/Analysis/call_once.cpp
@@ -0,0 +1,233 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -w -verify %s
+
+void clang_analyzer_eval(bool);
+
+// Faking std::std::call_once implementation.
+namespace std {
+typedef struct once_flag_s {
+  unsigned long __state_ = 0;
+} once_flag;
+
+template <class Callable, class... Args>
+void call_once(once_flag &o, Callable func, Args... args);
+} // namespace std
+
+// Check with Lambdas.
+void test_called_warning() {
+  std::once_flag g_initialize;
+  int z;
+
+  std::call_once(g_initialize, [&] {
+    int *x = nullptr;
+    int y = *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+    z = 200;
+  });
+}
+
+void test_called_on_path_inside_no_warning() {
+  std::once_flag g_initialize;
+
+  int *x = nullptr;
+  int y = 100;
+  int z;
+
+  std::call_once(g_initialize, [&] {
+    z = 200;
+    x = &z;
+  });
+
+  *x = 100; // no-warning
+  clang_analyzer_eval(z == 100); // expected-warning{{TRUE}}
+}
+
+void test_called_on_path_no_warning() {
+  std::once_flag g_initialize;
+
+  int *x = nullptr;
+  int y = 100;
+
+  std::call_once(g_initialize, [&] {
+    x = &y;
+  });
+
+  *x = 100; // no-warning
+}
+
+void test_called_on_path_warning() {
+  std::once_flag g_initialize;
+
+  int y = 100;
+  int *x = &y;
+
+  std::call_once(g_initialize, [&] {
+    x = nullptr;
+  });
+
+  *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+}
+
+void test_called_once_warning() {
+  std::once_flag g_initialize;
+
+  int *x = nullptr;
+  int y = 100;
+
+  std::call_once(g_initialize, [&] {
+    x = nullptr;
+  });
+
+  std::call_once(g_initialize, [&] {
+    x = &y;
+  });
+
+  *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+}
+
+void test_called_once_no_warning() {
+  std::once_flag g_initialize;
+
+  int *x = nullptr;
+  int y = 100;
+
+  std::call_once(g_initialize, [&] {
+    x = &y;
+  });
+
+  std::call_once(g_initialize, [&] {
+    x = nullptr;
+  });
+
+  *x = 100; // no-warning
+}
+
+static int global = 0;
+void funcPointer() {
+  global = 1;
+}
+
+void test_func_pointers() {
+  static std::once_flag flag;
+  std::call_once(flag, &funcPointer);
+  clang_analyzer_eval(global == 1); // expected-warning{{TRUE}}
+}
+
+template <class _Fp>
+class function; // undefined
+template <class _Rp, class... _ArgTypes>
+struct function<_Rp(_ArgTypes...)> {
+  _Rp operator()(_ArgTypes...) const;
+  template <class _Fp>
+  function(_Fp);
+};
+
+// Note: currently we do not support calls to std::function,
+// but the analyzer should not crash either.
+void test_function_objects_warning() {
+  int x = 0;
+  int *y = &x;
+
+  std::once_flag flag;
+
+  function<void()> func = [&]() {
+    y = nullptr;
+  };
+
+  std::call_once(flag, func);
+
+  func();
+  int z = *y;
+}
+
+void test_param_passing_lambda() {
+  std::once_flag flag;
+  int x = 120;
+  int y = 0;
+
+  std::call_once(flag, [&](int p) {
+    y = p;
+  },
+                 x);
+
+  clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
+}
+
+void test_param_passing_lambda_false() {
+  std::once_flag flag;
+  int x = 120;
+
+  std::call_once(flag, [&](int p) {
+    x = 0;
+  },
+                 x);
+
+  clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
+}
+
+void test_param_passing_stored_lambda() {
+  std::once_flag flag;
+  int x = 120;
+  int y = 0;
+
+  auto lambda = [&](int p) {
+    y = p;
+  };
+
+  std::call_once(flag, lambda, x);
+  clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
+}
+
+void test_multiparam_passing_lambda() {
+  std::once_flag flag;
+  int x = 120;
+
+  std::call_once(flag, [&](int a, int b, int c) {
+    x = a + b + c;
+  },
+                 1, 2, 3);
+
+  clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
+  clang_analyzer_eval(x == 6); // expected-warning{{TRUE}}
+}
+
+static int global2 = 0;
+void test_param_passing_lambda_global() {
+  std::once_flag flag;
+  global2 = 0;
+  std::call_once(flag, [&](int a, int b, int c) {
+    global2 = a + b + c;
+  },
+                 1, 2, 3);
+  clang_analyzer_eval(global2 == 6); // expected-warning{{TRUE}}
+}
+
+static int global3 = 0;
+void funcptr(int a, int b, int c) {
+  global3 = a + b + c;
+}
+
+void test_param_passing_funcptr() {
+  std::once_flag flag;
+  global3 = 0;
+
+  std::call_once(flag, &funcptr, 1, 2, 3);
+
+  clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}}
+}
+
+void test_blocks() {
+  global3 = 0;
+  std::once_flag flag;
+  std::call_once(flag, ^{
+    global3 = 120;
+  });
+  clang_analyzer_eval(global3 == 120); // expected-warning{{TRUE}}
+}
+
+int call_once() {
+  return 5;
+}
+
+void test_non_std_call_once() {
+  int x = call_once();
+  clang_analyzer_eval(x == 5); // expected-warning{{TRUE}}
+}