llvm/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp

// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s

#include "mock-types.h"

namespace WTF {

  constexpr unsigned long notFound = static_cast<unsigned long>(-1);

  class String;
  class StringImpl;

  class StringView {
  public:
    StringView(const String&);
  private:
    RefPtr<StringImpl> m_impl;
  };

  class StringImpl {
  public:
    void ref() const { ++m_refCount; }
    void deref() const {
      if (!--m_refCount)
        delete this;
    }

    static constexpr unsigned s_flagIs8Bit = 1u << 0;
    bool is8Bit() const { return m_hashAndFlags & s_flagIs8Bit; }
    const char* characters8() const { return m_char8; }
    const short* characters16() const { return m_char16; }
    unsigned length() const { return m_length; }
    Ref<StringImpl> substring(unsigned position, unsigned length) const;

    unsigned long find(char) const;
    unsigned long find(StringView) const;
    unsigned long contains(StringView) const;
    unsigned long findIgnoringASCIICase(StringView) const;

    bool startsWith(StringView) const;
    bool startsWithIgnoringASCIICase(StringView) const;
    bool endsWith(StringView) const;
    bool endsWithIgnoringASCIICase(StringView) const;

  private:
    mutable unsigned m_refCount { 0 };
    unsigned m_length { 0 };
    union {
      const char* m_char8;
      const short* m_char16;
    };
    unsigned m_hashAndFlags { 0 };
  };

  class String {
  public:
    String() = default;
    String(StringImpl& impl) : m_impl(&impl) { }
    String(StringImpl* impl) : m_impl(impl) { }
    String(Ref<StringImpl>&& impl) : m_impl(impl.get()) { }
    StringImpl* impl() { return m_impl.get(); }
    unsigned length() const { return m_impl ? m_impl->length() : 0; }
    const char* characters8() const { return m_impl ? m_impl->characters8() : nullptr; }
    const short* characters16() const { return m_impl ? m_impl->characters16() : nullptr; }

    bool is8Bit() const { return !m_impl || m_impl->is8Bit(); }

    unsigned long find(char character) const { return m_impl ? m_impl->find(character) : notFound; }
    unsigned long find(StringView str) const { return m_impl ? m_impl->find(str) : notFound; }
    unsigned long findIgnoringASCIICase(StringView) const;

    bool contains(char character) const { return find(character) != notFound; }
    bool contains(StringView) const;
    bool containsIgnoringASCIICase(StringView) const;

    bool startsWith(StringView) const;
    bool startsWithIgnoringASCIICase(StringView) const;
    bool endsWith(StringView) const;
    bool endsWithIgnoringASCIICase(StringView) const;

    String substring(unsigned position, unsigned length) const
    {
      if (!m_impl)
        return { };
      if (!position && length >= m_impl->length())
        return *this;
      return m_impl->substring(position, length);
    }

  private:
    RefPtr<StringImpl> m_impl;
  };

  template <typename T>
  class HashSet {
  public:
    template <typename U> T* find(U&) const;
    template <typename U> bool contains(U&) const;
    unsigned size() { return m_size; }
    template <typename U> void add(U&) const;
    template <typename U> void remove(U&) const;

  private:
    T* m_table { nullptr };
    unsigned m_size { 0 };
  };

  template <typename T, typename S>
  class HashMap {
  public:
    struct Item {
      T key;
      S value;
    };

    template <typename U> Item* find(U&) const;
    template <typename U> bool contains(U&) const;
    template <typename U> S* get(U&) const;
    template <typename U> S* inlineGet(U&) const;
    template <typename U> void add(U&) const;
    template <typename U> void remove(U&) const;

  private:
    Item* m_table { nullptr };
  };

  template <typename T>
  class WeakHashSet {
  public:
    template <typename U> T* find(U&) const;
    template <typename U> bool contains(U&) const;
    template <typename U> void add(U&) const;
    template <typename U> void remove(U&) const;
  };

  template <typename T>
  class Vector {
  public:
    unsigned size() { return m_size; }
    T& at(unsigned i) { return m_buffer[i]; }
    T& operator[](unsigned i) { return m_buffer[i]; }
    template <typename U> unsigned find(U&);
    template <typename U> unsigned reverseFind(U&);
    template <typename U> bool contains(U&);
    template <typename MatchFunction> unsigned findIf(const MatchFunction& match)
    {
      for (unsigned i = 0; i < m_size; ++i) {
        if (match(at(i)))
          return i;
      }
      return static_cast<unsigned>(-1);
    }
    template <typename MatchFunction> unsigned reverseFindIf(const MatchFunction& match)
    {
      for (unsigned i = 0; i < m_size; ++i) {
        if (match(at(m_size - i)))
          return i;
      }
      return static_cast<unsigned>(-1);
    }
    template <typename MatchFunction> bool containsIf(const MatchFunction& match)
    {
      for (unsigned i = 0; i < m_size; ++i) {
        if (match(at(m_size - i)))
          return true;
      }
      return false;
    }
    template <typename U> void append(U&) const;
    template <typename U> void remove(U&) const;

  private:
    T* m_buffer { nullptr };
    unsigned m_size { 0 };
  };

}

using WTF::StringView;
using WTF::StringImpl;
using WTF::String;
using WTF::HashSet;
using WTF::HashMap;
using WTF::WeakHashSet;
using WTF::Vector;

class RefCounted {
public:
  void ref() const;
  void deref() const;
};

RefCounted* object();
StringImpl* strImpl();
String* str();
StringView strView();

void test() {
  strImpl()->is8Bit();
  strImpl()->characters8();
  strImpl()->characters16();
  strImpl()->length();
  strImpl()->substring(2, 4);
  strImpl()->find(strView());
  strImpl()->contains(strView());
  strImpl()->findIgnoringASCIICase(strView());
  strImpl()->startsWith(strView());
  strImpl()->startsWithIgnoringASCIICase(strView());
  strImpl()->endsWith(strView());
  strImpl()->endsWithIgnoringASCIICase(strView());

  str()->is8Bit();
  str()->characters8();
  str()->characters16();
  str()->length();
  str()->substring(2, 4);
  str()->find(strView());
  str()->contains(strView());
  str()->findIgnoringASCIICase(strView());
  str()->startsWith(strView());
  str()->startsWithIgnoringASCIICase(strView());
  str()->endsWith(strView());
  str()->endsWithIgnoringASCIICase(strView());

  HashSet<RefPtr<RefCounted>> set;
  set.find(*object());
  set.contains(*object());
  set.add(*object());
  // expected-warning@-1{{Call argument is uncounted and unsafe}}
  set.remove(*object());
  // expected-warning@-1{{Call argument is uncounted and unsafe}}

  HashMap<Ref<RefCounted>, unsigned> map;
  map.find(*object());
  map.contains(*object());
  map.inlineGet(*object());
  map.add(*object());
  // expected-warning@-1{{Call argument is uncounted and unsafe}}
  map.remove(*object());
  // expected-warning@-1{{Call argument is uncounted and unsafe}}

  WeakHashSet<Ref<RefCounted>> weakSet;
  weakSet.find(*object());
  weakSet.contains(*object());
  weakSet.add(*object());
  // expected-warning@-1{{Call argument is uncounted and unsafe}}
  weakSet.remove(*object());
  // expected-warning@-1{{Call argument is uncounted and unsafe}}

  Vector<Ref<RefCounted>> vector;
  vector.at(0);
  vector[0];
  vector.find(*object());
  vector.reverseFind(*object());
  vector.contains(*object());
  vector.append(*object());
  // expected-warning@-1{{Call argument is uncounted and unsafe}}
  vector.remove(*object());
  // expected-warning@-1{{Call argument is uncounted and unsafe}}

  auto* obj = object();
  vector.findIf([&](Ref<RefCounted> key) { return key.ptr() == obj; });
  vector.reverseFindIf([&](Ref<RefCounted> key) { return key.ptr() == obj; });
  vector.containsIf([&](Ref<RefCounted> key) { return key.ptr() == obj; });
}