1 #include <cstddef>
2 #include <utility>
3 
4 #define MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG __attribute__((annotate("moz_must_return_from_caller_if_this_is_arg")))
5 #define MOZ_MAY_CALL_AFTER_MUST_RETURN __attribute__((annotate("moz_may_call_after_must_return")))
6 
7 struct Thrower {
ThrowThrower8   void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG Throw() {}
9 };
10 
11 void DoAnythingElse();
12 int MakeAnInt();
13 int MOZ_MAY_CALL_AFTER_MUST_RETURN SafeMakeInt();
14 bool Condition();
15 
16 // It might be nicer to #include "mozilla/ScopeExit.h" and use that here -- but
17 // doing so also will #define the two attribute-macros defined above, running a
18 // risk of redefinition errors.  Just stick to the normal clang-plugin test
19 // style and use as little external code as possible.
20 
21 template<typename Func>
22 class ScopeExit {
23   Func exitFunction;
24   bool callOnDestruction;
25 public:
ScopeExit(Func && func)26   explicit ScopeExit(Func&& func)
27     : exitFunction(std::move(func))
28     , callOnDestruction(true)
29   {}
30 
~ScopeExit()31   ~ScopeExit() {
32     if (callOnDestruction) {
33       exitFunction();
34     }
35   }
36 
release()37   void release() { callOnDestruction = false; }
38 };
39 
40 template<typename ExitFunction>
41 ScopeExit<ExitFunction>
MakeScopeExit(ExitFunction && func)42 MakeScopeExit(ExitFunction&& func)
43 {
44   return ScopeExit<ExitFunction>(std::move(func));
45 }
46 
47 class Foo {
48 public:
49   __attribute__((annotate("moz_implicit"))) Foo(std::nullptr_t);
50   Foo();
51 };
52 
a1(Thrower & thrower)53 void a1(Thrower& thrower) {
54   thrower.Throw();
55 }
56 
a2(Thrower & thrower)57 int a2(Thrower& thrower) {
58   thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
59   return MakeAnInt();
60 }
61 
a3(Thrower & thrower)62 int a3(Thrower& thrower) {
63   // RAII operations happening after a must-immediately-return are fine.
64   auto atExit = MakeScopeExit([] { DoAnythingElse(); });
65   thrower.Throw();
66   return 5;
67 }
68 
a4(Thrower & thrower)69 int a4(Thrower& thrower) {
70   thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
71   return Condition() ? MakeAnInt() : MakeAnInt();
72 }
73 
a5(Thrower & thrower)74 void a5(Thrower& thrower) {
75   thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
76   DoAnythingElse();
77 }
78 
a6(Thrower & thrower)79 int a6(Thrower& thrower) {
80   thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
81   DoAnythingElse();
82   return MakeAnInt();
83 }
84 
a7(Thrower & thrower)85 int a7(Thrower& thrower) {
86   thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
87   DoAnythingElse();
88   return Condition() ? MakeAnInt() : MakeAnInt();
89 }
90 
a8(Thrower & thrower)91 int a8(Thrower& thrower) {
92   thrower.Throw();
93   return SafeMakeInt();
94 }
95 
a9(Thrower & thrower)96 int a9(Thrower& thrower) {
97   if (Condition()) {
98     thrower.Throw();
99   }
100   return SafeMakeInt();
101 }
102 
a10(Thrower & thrower)103 int a10(Thrower& thrower) {
104   auto atExit = MakeScopeExit([] { DoAnythingElse(); });
105 
106   if (Condition()) {
107     thrower.Throw();
108     return SafeMakeInt();
109   }
110 
111   atExit.release();
112   DoAnythingElse();
113   return 5;
114 }
115 
b1(Thrower & thrower)116 void b1(Thrower& thrower) {
117   if (Condition()) {
118     thrower.Throw();
119   }
120 }
121 
b2(Thrower & thrower)122 int b2(Thrower& thrower) {
123   if (Condition()) {
124     thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
125   }
126   return MakeAnInt();
127 }
128 
b3(Thrower & thrower)129 int b3(Thrower& thrower) {
130   if (Condition()) {
131     thrower.Throw();
132   }
133   return 5;
134 }
135 
136 // Explicit test in orer to also verify the `UnaryOperator` node in the `CFG`
b3a(Thrower & thrower)137 int b3a(Thrower& thrower) {
138   if (Condition()) {
139     thrower.Throw();
140   }
141   return -1;
142 }
143 
b3b(Thrower & thrower)144 float b3b(Thrower& thrower) {
145   if (Condition()) {
146     thrower.Throw();
147   }
148   return 1.0f;
149 }
150 
b3c(Thrower & thrower)151 bool b3c(Thrower& thrower) {
152   if (Condition()) {
153     thrower.Throw();
154   }
155   return false;
156 }
157 
b4(Thrower & thrower)158 int b4(Thrower& thrower) {
159   if (Condition()) {
160     thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
161   }
162   return Condition() ? MakeAnInt() : MakeAnInt();
163 }
164 
b5(Thrower & thrower)165 void b5(Thrower& thrower) {
166   if (Condition()) {
167     thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
168   }
169   DoAnythingElse();
170 }
171 
b6(Thrower & thrower)172 void b6(Thrower& thrower) {
173   if (Condition()) {
174     thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
175     DoAnythingElse();
176   }
177 }
178 
b7(Thrower & thrower)179 void b7(Thrower& thrower) {
180   if (Condition()) {
181     thrower.Throw();
182     return;
183   }
184   DoAnythingElse();
185 }
186 
b8(Thrower & thrower)187 void b8(Thrower& thrower) {
188   if (Condition()) {
189     thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
190     DoAnythingElse();
191     return;
192   }
193   DoAnythingElse();
194 }
195 
b9(Thrower & thrower)196 void b9(Thrower& thrower) {
197   while (Condition()) {
198     thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
199   }
200 }
201 
b10(Thrower & thrower)202 void b10(Thrower& thrower) {
203   while (Condition()) {
204     thrower.Throw();
205     return;
206   }
207 }
208 
b11(Thrower & thrower)209 void b11(Thrower& thrower) {
210   thrower.Throw(); // expected-error {{You must immediately return after calling this function}}
211   if (Condition()) {
212     return;
213   } else {
214     return;
215   }
216 }
217 
b12(Thrower & thrower)218 void b12(Thrower& thrower) {
219   switch (MakeAnInt()) {
220   case 1:
221     break;
222   default:
223     thrower.Throw();
224     return;
225   }
226 }
227 
b13(Thrower & thrower)228 void b13(Thrower& thrower) {
229   if (Condition()) {
230     thrower.Throw();
231   }
232   return;
233 }
234 
b14(Thrower & thrower)235 Foo b14(Thrower& thrower) {
236   if (Condition()) {
237     thrower.Throw();
238     return nullptr;
239   }
240   return nullptr;
241 }
242 
b15(Thrower & thrower)243 Foo b15(Thrower& thrower) {
244   if (Condition()) {
245     thrower.Throw();
246   }
247   return nullptr;
248 }
249 
b16(Thrower & thrower)250 Foo b16(Thrower& thrower) {
251   if (Condition()) {
252     thrower.Throw();
253   }
254   return Foo();
255 }
256 
c1()257 void c1() {
258   Thrower thrower;
259   thrower.Throw();
260   DoAnythingElse(); // Should be allowed, since our thrower is not an arg
261 }
262 
263 class TestRet {
b13(Thrower & thrower)264   TestRet *b13(Thrower &thrower) {
265     if (Condition()) {
266       thrower.Throw();
267     }
268     return this;
269   }
270 };
271