// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
#define BASE_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_

#include "base/ref_counted.h"
#include "base/tuple.h"

// It is dangerous to post a task with a raw pointer argument to a function
// that expects a scoped_refptr<>.  The compiler will happily accept the
// situation, but it will not attempt to increase the refcount until the task
// runs.  Callers expecting the argument to be refcounted up at post time are
// in for a nasty surprise!  Example: http://crbug.com/27191
// The following set of traits are designed to generate a compile error
// whenever this antipattern is attempted.
template <class A, class B>
struct ExpectsScopedRefptrButGetsRawPtr {
  enum { value = 0 };
};

template <class A, class B>
struct ExpectsScopedRefptrButGetsRawPtr<scoped_refptr<A>, B*> {
  enum { value = 1 };
};

template <class Function, class Params>
struct FunctionUsesScopedRefptrCorrectly {
  enum { value = 1 };
};

template <class A1, class A2>
struct FunctionUsesScopedRefptrCorrectly<void (*)(A1), Tuple1<A2> > {
  enum { value = !ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value };
};

template <class A1, class B1, class A2, class B2>
struct FunctionUsesScopedRefptrCorrectly<void (*)(A1, B1), Tuple2<A2, B2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value) };
};

template <class A1, class B1, class C1, class A2, class B2, class C2>
struct FunctionUsesScopedRefptrCorrectly<void (*)(A1, B1, C1),
                                         Tuple3<A2, B2, C2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<C1, C2>::value) };
};

template <class A1, class B1, class C1, class D1,
          class A2, class B2, class C2, class D2>
struct FunctionUsesScopedRefptrCorrectly<void (*)(A1, B1, C1, D1),
                                         Tuple4<A2, B2, C2, D2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<C1, C2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<D1, D2>::value) };
};

template <class A1, class B1, class C1, class D1, class E1,
          class A2, class B2, class C2, class D2, class E2>
struct FunctionUsesScopedRefptrCorrectly<void (*)(A1, B1, C1, D1, E1),
                                         Tuple5<A2, B2, C2, D2, E2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<C1, C2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<D1, D2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<E1, E2>::value) };
};

template <class A1, class B1, class C1, class D1, class E1, class F1,
          class A2, class B2, class C2, class D2, class E2, class F2>
struct FunctionUsesScopedRefptrCorrectly<void (*)(A1, B1, C1, D1, E1, F1),
                                         Tuple6<A2, B2, C2, D2, E2, F2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<C1, C2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<D1, D2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<E1, E2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<F1, F2>::value) };
};

template <class A1, class B1, class C1, class D1, class E1, class F1, class G1,
          class A2, class B2, class C2, class D2, class E2, class F2, class G2>
struct FunctionUsesScopedRefptrCorrectly<void (*)(A1, B1, C1, D1, E1, F1, G1),
                                         Tuple7<A2, B2, C2, D2, E2, F2, G2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<C1, C2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<D1, D2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<E1, E2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<F1, F2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<G1, G2>::value) };
};

template <class Method, class Params>
struct MethodUsesScopedRefptrCorrectly {
  enum { value = 1 };
};

template <class T, class A1, class A2>
struct MethodUsesScopedRefptrCorrectly<void (T::*)(A1), Tuple1<A2> > {
  enum { value = !ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value };
};

template <class T, class A1, class B1, class A2, class B2>
struct MethodUsesScopedRefptrCorrectly<void (T::*)(A1, B1), Tuple2<A2, B2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value) };
};

template <class T, class A1, class B1, class C1,
                   class A2, class B2, class C2>
struct MethodUsesScopedRefptrCorrectly<void (T::*)(A1, B1, C1),
                                       Tuple3<A2, B2, C2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<C1, C2>::value) };
};

template <class T, class A1, class B1, class C1, class D1,
          class A2, class B2, class C2, class D2>
struct MethodUsesScopedRefptrCorrectly<void (T::*)(A1, B1, C1, D1),
                                       Tuple4<A2, B2, C2, D2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<C1, C2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<D1, D2>::value) };
};

template <class T, class A1, class B1, class C1, class D1, class E1,
                   class A2, class B2, class C2, class D2, class E2>
struct MethodUsesScopedRefptrCorrectly<void (T::*)(A1, B1, C1, D1, E1),
                                       Tuple5<A2, B2, C2, D2, E2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<C1, C2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<D1, D2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<E1, E2>::value) };
};

template <class T, class A1, class B1, class C1, class D1, class E1, class F1,
                   class A2, class B2, class C2, class D2, class E2, class F2>
struct MethodUsesScopedRefptrCorrectly<void (T::*)(A1, B1, C1, D1, E1, F1),
                                       Tuple6<A2, B2, C2, D2, E2, F2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<C1, C2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<D1, D2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<E1, E2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<F1, F2>::value) };
};

template <class T,
          class A1, class B1, class C1, class D1, class E1, class F1, class G1,
          class A2, class B2, class C2, class D2, class E2, class F2, class G2>
struct MethodUsesScopedRefptrCorrectly<void (T::*)(A1, B1, C1, D1, E1, F1, G1),
                                       Tuple7<A2, B2, C2, D2, E2, F2, G2> > {
  enum { value = !(ExpectsScopedRefptrButGetsRawPtr<A1, A2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<B1, B2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<C1, C2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<D1, D2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<E1, E2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<F1, F2>::value ||
                   ExpectsScopedRefptrButGetsRawPtr<G1, G2>::value) };
};

#endif  // BASE_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_