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)42MakeScopeExit(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)53void a1(Thrower& thrower) { 54 thrower.Throw(); 55 } 56 a2(Thrower & thrower)57int a2(Thrower& thrower) { 58 thrower.Throw(); // expected-error {{You must immediately return after calling this function}} 59 return MakeAnInt(); 60 } 61 a3(Thrower & thrower)62int 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)69int 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)74void a5(Thrower& thrower) { 75 thrower.Throw(); // expected-error {{You must immediately return after calling this function}} 76 DoAnythingElse(); 77 } 78 a6(Thrower & thrower)79int 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)85int 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)91int a8(Thrower& thrower) { 92 thrower.Throw(); 93 return SafeMakeInt(); 94 } 95 a9(Thrower & thrower)96int a9(Thrower& thrower) { 97 if (Condition()) { 98 thrower.Throw(); 99 } 100 return SafeMakeInt(); 101 } 102 a10(Thrower & thrower)103int 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)116void b1(Thrower& thrower) { 117 if (Condition()) { 118 thrower.Throw(); 119 } 120 } 121 b2(Thrower & thrower)122int 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)129int 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)137int b3a(Thrower& thrower) { 138 if (Condition()) { 139 thrower.Throw(); 140 } 141 return -1; 142 } 143 b3b(Thrower & thrower)144float b3b(Thrower& thrower) { 145 if (Condition()) { 146 thrower.Throw(); 147 } 148 return 1.0f; 149 } 150 b3c(Thrower & thrower)151bool b3c(Thrower& thrower) { 152 if (Condition()) { 153 thrower.Throw(); 154 } 155 return false; 156 } 157 b4(Thrower & thrower)158int 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)165void 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)172void 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)179void b7(Thrower& thrower) { 180 if (Condition()) { 181 thrower.Throw(); 182 return; 183 } 184 DoAnythingElse(); 185 } 186 b8(Thrower & thrower)187void 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)196void 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)202void b10(Thrower& thrower) { 203 while (Condition()) { 204 thrower.Throw(); 205 return; 206 } 207 } 208 b11(Thrower & thrower)209void 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)218void b12(Thrower& thrower) { 219 switch (MakeAnInt()) { 220 case 1: 221 break; 222 default: 223 thrower.Throw(); 224 return; 225 } 226 } 227 b13(Thrower & thrower)228void b13(Thrower& thrower) { 229 if (Condition()) { 230 thrower.Throw(); 231 } 232 return; 233 } 234 b14(Thrower & thrower)235Foo b14(Thrower& thrower) { 236 if (Condition()) { 237 thrower.Throw(); 238 return nullptr; 239 } 240 return nullptr; 241 } 242 b15(Thrower & thrower)243Foo b15(Thrower& thrower) { 244 if (Condition()) { 245 thrower.Throw(); 246 } 247 return nullptr; 248 } 249 b16(Thrower & thrower)250Foo b16(Thrower& thrower) { 251 if (Condition()) { 252 thrower.Throw(); 253 } 254 return Foo(); 255 } 256 c1()257void 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