| // 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; }); |
| } |