// RUN: %clang_cc1 -analyze -std=c++11 -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-inlining=destructors -analyzer-config c++-container-inlining=false -verify %s
// RUN: %clang_cc1 -analyze -std=c++11 -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-inlining=destructors -analyzer-config c++-container-inlining=true -DINLINE=1 -verify %s

#ifndef HEADER

void clang_analyzer_eval(bool);
void clang_analyzer_checkInlined(bool);

#define HEADER
#include "containers.cpp"
#undef HEADER

void test() {
  MySet set(0);

  clang_analyzer_eval(set.isEmpty());
#if INLINE
  // expected-warning@-2 {{TRUE}}
#else
  // expected-warning@-4 {{UNKNOWN}}
#endif

  clang_analyzer_eval(set.raw_begin() == set.raw_end());
#if INLINE
  // expected-warning@-2 {{TRUE}}
#else
  // expected-warning@-4 {{UNKNOWN}}
#endif

  clang_analyzer_eval(set.begin().impl == set.end().impl);
#if INLINE
  // expected-warning@-2 {{TRUE}}
#else
  // expected-warning@-4 {{UNKNOWN}}
#endif
}

void testSubclass(MySetSubclass &sub) {
  sub.useIterator(sub.begin());

  MySetSubclass local;
}

void testWrappers(BeginOnlySet &w1, IteratorStructOnlySet &w2,
                  IteratorTypedefOnlySet &w3, IteratorUsingOnlySet &w4) {
  BeginOnlySet local1;
  IteratorStructOnlySet local2;
  IteratorTypedefOnlySet local3;
  IteratorUsingOnlySet local4;

  clang_analyzer_eval(w1.begin().impl.impl == w1.begin().impl.impl);
#if INLINE
  // expected-warning@-2 {{TRUE}}
#else
  // expected-warning@-4 {{UNKNOWN}}
#endif

  clang_analyzer_eval(w2.start().impl == w2.start().impl);
#if INLINE
  // expected-warning@-2 {{TRUE}}
#else
  // expected-warning@-4 {{UNKNOWN}}
#endif

  clang_analyzer_eval(w3.start().impl == w3.start().impl);
#if INLINE
  // expected-warning@-2 {{TRUE}}
#else
  // expected-warning@-4 {{UNKNOWN}}
#endif

  clang_analyzer_eval(w4.start().impl == w4.start().impl);
#if INLINE
  // expected-warning@-2 {{TRUE}}
#else
  // expected-warning@-4 {{UNKNOWN}}
#endif
}


#else // HEADER

#include "../Inputs/system-header-simulator-cxx.h"

class MySet {
  int *storage;
  unsigned size;
public:
  MySet() : storage(0), size(0) {
    clang_analyzer_checkInlined(true);
#if INLINE
    // expected-warning@-2 {{TRUE}}
#endif
  }

  MySet(unsigned n) : storage(new int[n]), size(n) {
    clang_analyzer_checkInlined(true);
#if INLINE
    // expected-warning@-2 {{TRUE}}
#endif
  }

  ~MySet() { delete[] storage; }

  bool isEmpty() {
    clang_analyzer_checkInlined(true);
    #if INLINE
        // expected-warning@-2 {{TRUE}}
    #endif
    return size == 0;
  }

  struct iterator {
    int *impl;

    iterator(int *p) : impl(p) {}
  };

  iterator begin() {
    clang_analyzer_checkInlined(true);
    #if INLINE
        // expected-warning@-2 {{TRUE}}
    #endif
    return iterator(storage);
  }

  iterator end() {
    clang_analyzer_checkInlined(true);
    #if INLINE
        // expected-warning@-2 {{TRUE}}
    #endif
    return iterator(storage+size);
  }

  typedef int *raw_iterator;

  raw_iterator raw_begin() {
    clang_analyzer_checkInlined(true);
    #if INLINE
        // expected-warning@-2 {{TRUE}}
    #endif
    return storage;
  }
  raw_iterator raw_end() {
    clang_analyzer_checkInlined(true);
    #if INLINE
        // expected-warning@-2 {{TRUE}}
    #endif
    return storage + size;
  }
};

class MySetSubclass : public MySet {
public:
  MySetSubclass() {
    clang_analyzer_checkInlined(true);
#if INLINE
    // expected-warning@-2 {{TRUE}}
#endif
  }

  void useIterator(iterator i) {
    clang_analyzer_checkInlined(true);
    #if INLINE
        // expected-warning@-2 {{TRUE}}
    #endif
  }
};

class BeginOnlySet {
  MySet impl;
public:
  struct IterImpl {
    MySet::iterator impl;
    typedef std::forward_iterator_tag iterator_category;

    IterImpl(MySet::iterator i) : impl(i) {
      clang_analyzer_checkInlined(true);
#if INLINE
      // expected-warning@-2 {{TRUE}}
#endif
    }
  };

  BeginOnlySet() {
    clang_analyzer_checkInlined(true);
#if INLINE
    // expected-warning@-2 {{TRUE}}
#endif
  }

  typedef IterImpl wrapped_iterator;

  wrapped_iterator begin() {
    clang_analyzer_checkInlined(true);
    #if INLINE
        // expected-warning@-2 {{TRUE}}
    #endif
    return IterImpl(impl.begin());
  }
};

class IteratorTypedefOnlySet {
  MySet impl;
public:

  IteratorTypedefOnlySet() {
    clang_analyzer_checkInlined(true);
#if INLINE
    // expected-warning@-2 {{TRUE}}
#endif
  }

  typedef MySet::iterator iterator;

  iterator start() {
    clang_analyzer_checkInlined(true);
#if INLINE
    // expected-warning@-2 {{TRUE}}
#endif
    return impl.begin();
  }
};

class IteratorUsingOnlySet {
  MySet impl;
public:

  IteratorUsingOnlySet() {
    clang_analyzer_checkInlined(true);
#if INLINE
    // expected-warning@-2 {{TRUE}}
#endif
  }

  using iterator = MySet::iterator;

  iterator start() {
    clang_analyzer_checkInlined(true);
    #if INLINE
        // expected-warning@-2 {{TRUE}}
    #endif
    return impl.begin();
  }
};

class IteratorStructOnlySet {
  MySet impl;
public:

  IteratorStructOnlySet() {
    clang_analyzer_checkInlined(true);
#if INLINE
    // expected-warning@-2 {{TRUE}}
#endif
  }

  struct iterator {
    int *impl;
  };

  iterator start() {
    clang_analyzer_checkInlined(true);
    #if INLINE
        // expected-warning@-2 {{TRUE}}
    #endif
    return iterator{impl.begin().impl};
  }
};

#endif // HEADER
