Bug 920292 - Check the types of assert conditions - r=froydnj
authorBenoit Jacob <bjacob@mozilla.com>
Wed, 30 Apr 2014 22:39:13 -0400
changeset 181125 6349837631e5896136de186a30118341ca869fe8
parent 181124 bf7655b240091c96c24197abca53cc16fd37efbd
child 181126 9c8fbf297d51b26267089cdcf767de56d4299d7c
push id26693
push useremorley@mozilla.com
push dateThu, 01 May 2014 14:50:08 +0000
treeherdermozilla-central@51bc58066ac9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs920292
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 920292 - Check the types of assert conditions - r=froydnj
mfbt/Assertions.h
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -275,27 +275,86 @@ MOZ_ReportCrash(const char* s, const cha
  *   MOZ_ASSERT(getSlot(PRIMITIVE_THIS_SLOT).isUndefined(),
  *              "we already set [[PrimitiveThis]] for this String object");
  *
  * MOZ_ASSERT has no effect in non-debug builds.  It is designed to catch bugs
  * *only* during debugging, not "in the field". If you want the latter, use
  * MOZ_RELEASE_ASSERT, which applies to non-debug builds as well.
  */
 
+/*
+ * Implement MOZ_VALIDATE_ASSERT_CONDITION_TYPE, which is used to guard against
+ * accidentally passing something unintended in lieu of an assertion condition.
+ */
+
+#ifdef __cplusplus
+#  if defined(__clang__)
+#    define MOZ_SUPPORT_ASSERT_CONDITION_TYPE_VALIDATION
+#  elif defined(__GNUC__)
+//   B2G GCC 4.4 has insufficient decltype support.
+#    if MOZ_GCC_VERSION_AT_LEAST(4, 5, 0)
+#      define MOZ_SUPPORT_ASSERT_CONDITION_TYPE_VALIDATION
+#    endif
+#  elif defined(_MSC_VER)
+//   Disabled for now because of insufficient decltype support. Bug 1004028.
+#  endif
+#endif
+
+#ifdef MOZ_SUPPORT_ASSERT_CONDITION_TYPE_VALIDATION
+#  include "mozilla/TypeTraits.h"
+namespace mozilla {
+namespace detail {
+
+template<typename T>
+struct IsFunction
+{
+    static const bool value = false;
+};
+
+template<typename R, typename... A>
+struct IsFunction<R(A...)>
+{
+    static const bool value = true;
+};
+
+template<typename T>
+void ValidateAssertConditionType()
+{
+  typedef typename RemoveReference<T>::Type ValueT;
+  static_assert(!IsArray<ValueT>::value,
+                "Expected boolean assertion condition, got an array or a string!");
+  static_assert(!IsFunction<ValueT>::value,
+                "Expected boolean assertion condition, got a function! Did you intend to call that function?");
+  static_assert(!IsFloatingPoint<ValueT>::value,
+                "It's often a bad idea to assert that a floating-point number is nonzero, "
+                "because such assertions tend to intermittently fail. Shouldn't your code gracefully handle "
+                "this case instead of asserting? Anyway, if you really want to "
+                "do that, write an explicit boolean condition, like !!x or x!=0.");
+}
+
+} // namespace detail
+} // namespace mozilla
+#  define MOZ_VALIDATE_ASSERT_CONDITION_TYPE(x) mozilla::detail::ValidateAssertConditionType<decltype(x)>()
+#else
+#  define MOZ_VALIDATE_ASSERT_CONDITION_TYPE(x)
+#endif
+
 /* First the single-argument form. */
 #define MOZ_ASSERT_HELPER1(expr) \
    do { \
+     MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
      if (MOZ_UNLIKELY(!(expr))) { \
        MOZ_ReportAssertionFailure(#expr, __FILE__, __LINE__); \
        MOZ_REALLY_CRASH(); \
      } \
    } while (0)
 /* Now the two-argument form. */
 #define MOZ_ASSERT_HELPER2(expr, explain) \
    do { \
+     MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
      if (MOZ_UNLIKELY(!(expr))) { \
        MOZ_ReportAssertionFailure(#expr " (" explain ")", __FILE__, __LINE__); \
        MOZ_REALLY_CRASH(); \
      } \
    } while (0)
 
 #define MOZ_RELEASE_ASSERT_GLUE(a, b) a b
 #define MOZ_RELEASE_ASSERT(...) \