xref: /llvm-project/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp (revision df91cde4da62aec22e4d384b1bc800590c7f561a)
1 // RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
2 
3 #include "mock-types.h"
4 
5 namespace WTF {
6 
7   constexpr unsigned long notFound = static_cast<unsigned long>(-1);
8 
9   class String;
10   class StringImpl;
11 
12   class StringView {
13   public:
14     StringView(const String&);
15   private:
16     RefPtr<StringImpl> m_impl;
17   };
18 
19   class StringImpl {
20   public:
ref() const21     void ref() const { ++m_refCount; }
deref() const22     void deref() const {
23       if (!--m_refCount)
24         delete this;
25     }
26 
27     static constexpr unsigned s_flagIs8Bit = 1u << 0;
is8Bit() const28     bool is8Bit() const { return m_hashAndFlags & s_flagIs8Bit; }
characters8() const29     const char* characters8() const { return m_char8; }
characters16() const30     const short* characters16() const { return m_char16; }
length() const31     unsigned length() const { return m_length; }
32     Ref<StringImpl> substring(unsigned position, unsigned length) const;
33 
34     unsigned long find(char) const;
35     unsigned long find(StringView) const;
36     unsigned long contains(StringView) const;
37     unsigned long findIgnoringASCIICase(StringView) const;
38 
39     bool startsWith(StringView) const;
40     bool startsWithIgnoringASCIICase(StringView) const;
41     bool endsWith(StringView) const;
42     bool endsWithIgnoringASCIICase(StringView) const;
43 
44   private:
45     mutable unsigned m_refCount { 0 };
46     unsigned m_length { 0 };
47     union {
48       const char* m_char8;
49       const short* m_char16;
50     };
51     unsigned m_hashAndFlags { 0 };
52   };
53 
54   class String {
55   public:
56     String() = default;
String(StringImpl & impl)57     String(StringImpl& impl) : m_impl(&impl) { }
String(StringImpl * impl)58     String(StringImpl* impl) : m_impl(impl) { }
String(Ref<StringImpl> && impl)59     String(Ref<StringImpl>&& impl) : m_impl(impl.get()) { }
impl()60     StringImpl* impl() { return m_impl.get(); }
length() const61     unsigned length() const { return m_impl ? m_impl->length() : 0; }
characters8() const62     const char* characters8() const { return m_impl ? m_impl->characters8() : nullptr; }
characters16() const63     const short* characters16() const { return m_impl ? m_impl->characters16() : nullptr; }
64 
is8Bit() const65     bool is8Bit() const { return !m_impl || m_impl->is8Bit(); }
66 
find(char character) const67     unsigned long find(char character) const { return m_impl ? m_impl->find(character) : notFound; }
find(StringView str) const68     unsigned long find(StringView str) const { return m_impl ? m_impl->find(str) : notFound; }
69     unsigned long findIgnoringASCIICase(StringView) const;
70 
contains(char character) const71     bool contains(char character) const { return find(character) != notFound; }
72     bool contains(StringView) const;
73     bool containsIgnoringASCIICase(StringView) const;
74 
75     bool startsWith(StringView) const;
76     bool startsWithIgnoringASCIICase(StringView) const;
77     bool endsWith(StringView) const;
78     bool endsWithIgnoringASCIICase(StringView) const;
79 
substring(unsigned position,unsigned length) const80     String substring(unsigned position, unsigned length) const
81     {
82       if (!m_impl)
83         return { };
84       if (!position && length >= m_impl->length())
85         return *this;
86       return m_impl->substring(position, length);
87     }
88 
89   private:
90     RefPtr<StringImpl> m_impl;
91   };
92 
93   template <typename T>
94   class HashSet {
95   public:
96     template <typename U> T* find(U&) const;
97     template <typename U> bool contains(U&) const;
size()98     unsigned size() { return m_size; }
99     template <typename U> void add(U&) const;
100     template <typename U> void remove(U&) const;
101 
102   private:
103     T* m_table { nullptr };
104     unsigned m_size { 0 };
105   };
106 
107   template <typename T, typename S>
108   class HashMap {
109   public:
110     struct Item {
111       T key;
112       S value;
113     };
114 
115     template <typename U> Item* find(U&) const;
116     template <typename U> bool contains(U&) const;
117     template <typename U> S* get(U&) const;
118     template <typename U> S* inlineGet(U&) const;
119     template <typename U> void add(U&) const;
120     template <typename U> void remove(U&) const;
121 
122   private:
123     Item* m_table { nullptr };
124   };
125 
126   template <typename T>
127   class WeakHashSet {
128   public:
129     template <typename U> T* find(U&) const;
130     template <typename U> bool contains(U&) const;
131     template <typename U> void add(U&) const;
132     template <typename U> void remove(U&) const;
133   };
134 
135   template <typename T>
136   class Vector {
137   public:
size()138     unsigned size() { return m_size; }
at(unsigned i)139     T& at(unsigned i) { return m_buffer[i]; }
operator [](unsigned i)140     T& operator[](unsigned i) { return m_buffer[i]; }
141     template <typename U> unsigned find(U&);
142     template <typename U> unsigned reverseFind(U&);
143     template <typename U> bool contains(U&);
findIf(const MatchFunction & match)144     template <typename MatchFunction> unsigned findIf(const MatchFunction& match)
145     {
146       for (unsigned i = 0; i < m_size; ++i) {
147         if (match(at(i)))
148           return i;
149       }
150       return static_cast<unsigned>(-1);
151     }
reverseFindIf(const MatchFunction & match)152     template <typename MatchFunction> unsigned reverseFindIf(const MatchFunction& match)
153     {
154       for (unsigned i = 0; i < m_size; ++i) {
155         if (match(at(m_size - i)))
156           return i;
157       }
158       return static_cast<unsigned>(-1);
159     }
containsIf(const MatchFunction & match)160     template <typename MatchFunction> bool containsIf(const MatchFunction& match)
161     {
162       for (unsigned i = 0; i < m_size; ++i) {
163         if (match(at(m_size - i)))
164           return true;
165       }
166       return false;
167     }
168     template <typename U> void append(U&) const;
169     template <typename U> void remove(U&) const;
170 
171   private:
172     T* m_buffer { nullptr };
173     unsigned m_size { 0 };
174   };
175 
176 }
177 
178 using WTF::StringView;
179 using WTF::StringImpl;
180 using WTF::String;
181 using WTF::HashSet;
182 using WTF::HashMap;
183 using WTF::WeakHashSet;
184 using WTF::Vector;
185 
186 class RefCounted {
187 public:
188   void ref() const;
189   void deref() const;
190 };
191 
192 RefCounted* object();
193 StringImpl* strImpl();
194 String* str();
195 StringView strView();
196 
test()197 void test() {
198   strImpl()->is8Bit();
199   strImpl()->characters8();
200   strImpl()->characters16();
201   strImpl()->length();
202   strImpl()->substring(2, 4);
203   strImpl()->find(strView());
204   strImpl()->contains(strView());
205   strImpl()->findIgnoringASCIICase(strView());
206   strImpl()->startsWith(strView());
207   strImpl()->startsWithIgnoringASCIICase(strView());
208   strImpl()->endsWith(strView());
209   strImpl()->endsWithIgnoringASCIICase(strView());
210 
211   str()->is8Bit();
212   str()->characters8();
213   str()->characters16();
214   str()->length();
215   str()->substring(2, 4);
216   str()->find(strView());
217   str()->contains(strView());
218   str()->findIgnoringASCIICase(strView());
219   str()->startsWith(strView());
220   str()->startsWithIgnoringASCIICase(strView());
221   str()->endsWith(strView());
222   str()->endsWithIgnoringASCIICase(strView());
223 
224   HashSet<RefPtr<RefCounted>> set;
225   set.find(*object());
226   set.contains(*object());
227   set.add(*object());
228   // expected-warning@-1{{Call argument is uncounted and unsafe}}
229   set.remove(*object());
230   // expected-warning@-1{{Call argument is uncounted and unsafe}}
231 
232   HashMap<Ref<RefCounted>, unsigned> map;
233   map.find(*object());
234   map.contains(*object());
235   map.inlineGet(*object());
236   map.add(*object());
237   // expected-warning@-1{{Call argument is uncounted and unsafe}}
238   map.remove(*object());
239   // expected-warning@-1{{Call argument is uncounted and unsafe}}
240 
241   WeakHashSet<Ref<RefCounted>> weakSet;
242   weakSet.find(*object());
243   weakSet.contains(*object());
244   weakSet.add(*object());
245   // expected-warning@-1{{Call argument is uncounted and unsafe}}
246   weakSet.remove(*object());
247   // expected-warning@-1{{Call argument is uncounted and unsafe}}
248 
249   Vector<Ref<RefCounted>> vector;
250   vector.at(0);
251   vector[0];
252   vector.find(*object());
253   vector.reverseFind(*object());
254   vector.contains(*object());
255   vector.append(*object());
256   // expected-warning@-1{{Call argument is uncounted and unsafe}}
257   vector.remove(*object());
258   // expected-warning@-1{{Call argument is uncounted and unsafe}}
259 
260   auto* obj = object();
261   vector.findIf([&](Ref<RefCounted> key) { return key.ptr() == obj; });
262   vector.reverseFindIf([&](Ref<RefCounted> key) { return key.ptr() == obj; });
263   vector.containsIf([&](Ref<RefCounted> key) { return key.ptr() == obj; });
264 }