const ParmVarDecl *Flag = D->getParamDecl(0);
const ParmVarDecl *Callback = D->getParamDecl(1);
+
+ if (!Callback->getType()->isReferenceType()) {
+ llvm::dbgs() << "libcxx03 std::call_once implementation, skipping.\n";
+ return nullptr;
+ }
+ if (!Flag->getType()->isReferenceType()) {
+ llvm::dbgs() << "unknown std::call_once implementation, skipping.\n";
+ return nullptr;
+ }
+
QualType CallbackType = Callback->getType().getNonReferenceType();
// Nullable pointer, non-null iff function is a CXXRecordDecl.
// Otherwise, try libstdc++ implementation, with a field
// `_M_once`
if (!FlagFieldDecl) {
- DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag struct, "
- << "assuming libstdc++ implementation\n");
FlagFieldDecl = M.findMemberField(FlagRecordDecl, "_M_once");
}
if (!FlagFieldDecl) {
- DEBUG(llvm::dbgs() << "No field _M_once found on std::once flag struct: "
- << "unknown std::call_once implementation, "
- << "ignoring the call");
+ DEBUG(llvm::dbgs() << "No field _M_once or __state_ found on "
+ << "std::once_flag struct: unknown std::call_once "
+ << "implementation, ignoring the call.");
return nullptr;
}
// First two arguments are used for the flag and for the callback.
if (D->getNumParams() != CallbackFunctionType->getNumParams() + 2) {
- DEBUG(llvm::dbgs() << "Number of params of the callback does not match "
- << "the number of params passed to std::call_once, "
- << "ignoring the call");
+ DEBUG(llvm::dbgs() << "Types of params of the callback do not match "
+ << "params passed to std::call_once, "
+ << "ignoring the call\n");
return nullptr;
}
// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -verify %s
// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -DEMULATE_LIBSTDCPP -verify %s
+// We do NOT model libcxx03 implementation, but the analyzer should still
+// not crash.
+// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -DEMULATE_LIBCXX03 -verify %s
+// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -DEMULATE_LIBCXX03 -DEMULATE_LIBSTDCPP -verify %s
+
void clang_analyzer_eval(bool);
// Faking std::std::call_once implementation.
} once_flag;
#endif
+#ifndef EMULATE_LIBCXX03
template <class Callable, class... Args>
void call_once(once_flag &o, Callable&& func, Args&&... args) {};
+#else
+template <class Callable, class... Args> // libcxx03 call_once
+void call_once(once_flag &o, Callable func, Args&&... args) {};
+#endif
} // namespace std
std::call_once(g_initialize, [&] {
int *x = nullptr;
+#ifndef EMULATE_LIBCXX03
int y = *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+#endif
z = 200;
});
}
x = &z;
});
+#ifndef EMULATE_LIBCXX03
*x = 100; // no-warning
clang_analyzer_eval(z == 100); // expected-warning{{TRUE}}
+#endif
}
void test_called_on_path_no_warning() {
x = &y;
});
+#ifndef EMULATE_LIBCXX03
*x = 100; // no-warning
+#else
+ *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+#endif
}
void test_called_on_path_warning() {
x = nullptr;
});
+#ifndef EMULATE_LIBCXX03
*x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+#endif
}
void test_called_once_warning() {
x = &y;
});
+#ifndef EMULATE_LIBCXX03
*x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+#endif
}
void test_called_once_no_warning() {
x = nullptr;
});
+#ifndef EMULATE_LIBCXX03
*x = 100; // no-warning
+#endif
}
static int global = 0;
void test_func_pointers() {
static std::once_flag flag;
std::call_once(flag, &funcPointer);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(global == 1); // expected-warning{{TRUE}}
+#endif
}
template <class _Fp>
},
x);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
+#endif
}
void test_param_passing_lambda_false() {
},
x);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
+#endif
}
void test_param_passing_stored_lambda() {
};
std::call_once(flag, lambda, x);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
+#endif
}
void test_multiparam_passing_lambda() {
},
1, 2, 3);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
clang_analyzer_eval(x == 6); // expected-warning{{TRUE}}
+#endif
}
static int global2 = 0;
global2 = a + b + c;
},
1, 2, 3);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(global2 == 6); // expected-warning{{TRUE}}
+#endif
}
static int global3 = 0;
std::call_once(flag, &funcptr, 1, 2, 3);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}}
+#endif
}
void test_blocks() {
std::call_once(flag, ^{
global3 = 120;
});
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(global3 == 120); // expected-warning{{TRUE}}
+#endif
}
int call_once() {
void test_non_std_call_once() {
int x = call_once();
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(x == 5); // expected-warning{{TRUE}}
+#endif
}
namespace std {
}
void g();
void test_no_segfault_on_different_impl() {
+#ifndef EMULATE_LIBCXX03
std::call_once(g, false); // no-warning
+#endif
}
void test_lambda_refcapture() {
static std::once_flag flag;
int a = 6;
std::call_once(flag, [&](int &a) { a = 42; }, a);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(a == 42); // expected-warning{{TRUE}}
+#endif
}
void test_lambda_refcapture2() {
static std::once_flag flag;
int a = 6;
std::call_once(flag, [=](int &a) { a = 42; }, a);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(a == 42); // expected-warning{{TRUE}}
+#endif
}
void test_lambda_fail_refcapture() {
static std::once_flag flag;
int a = 6;
std::call_once(flag, [=](int a) { a = 42; }, a);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(a == 42); // expected-warning{{FALSE}}
+#endif
}
void mutator(int ¶m) {
static std::once_flag flag;
int a = 6;
std::call_once(flag, &mutator, a);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(a == 42); // expected-warning{{TRUE}}
+#endif
}
void fail_mutator(int param) {
static std::once_flag flag;
int a = 6;
std::call_once(flag, &fail_mutator, a);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(a == 42); // expected-warning{{FALSE}}
+#endif
}
// Function is implicitly treated as a function pointer
static std::once_flag flagn;
std::call_once(flagn, callbackn, x);
+#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
+#endif
}