Bug 920292 - Check the types of assert conditions - r=froydnj
authorBenoit Jacob <bjacob@mozilla.com>
Wed, 30 Apr 2014 22:39:13 -0400
changeset 181530 6349837631e5896136de186a30118341ca869fe8
parent 181529 bf7655b240091c96c24197abca53cc16fd37efbd
child 181531 9c8fbf297d51b26267089cdcf767de56d4299d7c
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersfroydnj
bugs920292
milestone32.0a1
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(...) \