1 | // RUN: %clang_analyze_cc1 -fblocks -verify %s -analyzer-checker=core \ |
2 | // RUN: -analyzer-checker=nullability.NullPassedToNonnull \ |
3 | // RUN: -analyzer-checker=nullability.NullReturnedFromNonnull \ |
4 | // RUN: -analyzer-checker=nullability.NullablePassedToNonnull \ |
5 | // RUN: -analyzer-checker=nullability.NullableReturnedFromNonnull \ |
6 | // RUN: -analyzer-checker=nullability.NullableDereferenced \ |
7 | // RUN: -DNOSYSTEMHEADERS=0 |
8 | |
9 | // RUN: %clang_analyze_cc1 -fblocks -verify %s -analyzer-checker=core \ |
10 | // RUN: -analyzer-checker=nullability.NullPassedToNonnull \ |
11 | // RUN: -analyzer-checker=nullability.NullReturnedFromNonnull \ |
12 | // RUN: -analyzer-checker=nullability.NullablePassedToNonnull \ |
13 | // RUN: -analyzer-checker=nullability.NullableReturnedFromNonnull \ |
14 | // RUN: -analyzer-checker=nullability.NullableDereferenced \ |
15 | // RUN: -DNOSYSTEMHEADERS=1 \ |
16 | // RUN: -analyzer-config nullability:NoDiagnoseCallsToSystemHeaders=true |
17 | |
18 | // RUN: %clang_analyze_cc1 -fblocks -verify %s -analyzer-checker=core\ |
19 | // RUN: -analyzer-checker=nullability.NullPassedToNonnull\ |
20 | // RUN: -analyzer-checker=nullability.NullReturnedFromNonnull\ |
21 | // RUN: -analyzer-checker=nullability.NullablePassedToNonnull\ |
22 | // RUN: -analyzer-checker=nullability.NullableReturnedFromNonnull\ |
23 | // RUN: -analyzer-checker=nullability.NullableDereferenced\ |
24 | // RUN: -DNOSYSTEMHEADERS=0 -fobjc-arc |
25 | |
26 | // RUN: %clang_analyze_cc1 -fblocks -verify %s -analyzer-checker=core\ |
27 | // RUN: -analyzer-checker=nullability.NullPassedToNonnull\ |
28 | // RUN: -analyzer-checker=nullability.NullReturnedFromNonnull\ |
29 | // RUN: -analyzer-checker=nullability.NullablePassedToNonnull\ |
30 | // RUN: -analyzer-checker=nullability.NullableReturnedFromNonnull\ |
31 | // RUN: -analyzer-checker=nullability.NullableDereferenced\ |
32 | // RUN: -DNOSYSTEMHEADERS=1 -fobjc-arc\ |
33 | // RUN: -analyzer-config nullability:NoDiagnoseCallsToSystemHeaders=true |
34 | |
35 | #include "Inputs/system-header-simulator-for-nullability.h" |
36 | |
37 | @interface TestObject : NSObject |
38 | - (int *_Nonnull)returnsNonnull; |
39 | - (int *_Nullable)returnsNullable; |
40 | - (int *)returnsUnspecified; |
41 | - (void)takesNonnull:(int *_Nonnull)p; |
42 | - (void)takesNullable:(int *_Nullable)p; |
43 | - (void)takesUnspecified:(int *)p; |
44 | @property(readonly, strong) NSString *stuff; |
45 | @end |
46 | |
47 | TestObject * getUnspecifiedTestObject(); |
48 | TestObject *_Nonnull getNonnullTestObject(); |
49 | TestObject *_Nullable getNullableTestObject(); |
50 | |
51 | int getRandom(); |
52 | |
53 | typedef struct Dummy { int val; } Dummy; |
54 | |
55 | void takesNullable(Dummy *_Nullable); |
56 | void takesNonnull(Dummy *_Nonnull); |
57 | void takesUnspecified(Dummy *); |
58 | |
59 | Dummy *_Nullable returnsNullable(); |
60 | Dummy *_Nonnull returnsNonnull(); |
61 | Dummy *returnsUnspecified(); |
62 | int *_Nullable returnsNullableInt(); |
63 | |
64 | template <typename T> T *eraseNullab(T *p) { return p; } |
65 | |
66 | void takesAttrNonnull(Dummy *p) __attribute((nonnull(1))); |
67 | |
68 | void testBasicRules() { |
69 | Dummy *p = returnsNullable(); |
70 | int *ptr = returnsNullableInt(); |
71 | // Make every dereference a different path to avoid sinks after errors. |
72 | switch (getRandom()) { |
73 | case 0: { |
74 | Dummy &r = *p; // expected-warning {{Nullable pointer is dereferenced}} |
75 | } break; |
76 | case 1: { |
77 | int b = p->val; // expected-warning {{Nullable pointer is dereferenced}} |
78 | } break; |
79 | case 2: { |
80 | int stuff = *ptr; // expected-warning {{Nullable pointer is dereferenced}} |
81 | } break; |
82 | case 3: |
83 | takesNonnull(p); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} |
84 | break; |
85 | case 4: { |
86 | Dummy d; |
87 | takesNullable(&d); |
88 | Dummy dd(d); |
89 | break; |
90 | } |
91 | case 5: takesAttrNonnull(p); break; // expected-warning {{Nullable pointer is passed to}} |
92 | default: { Dummy d = *p; } break; // expected-warning {{Nullable pointer is dereferenced}} |
93 | } |
94 | if (p) { |
95 | takesNonnull(p); |
96 | if (getRandom()) { |
97 | Dummy &r = *p; |
98 | } else { |
99 | int b = p->val; |
100 | } |
101 | } |
102 | Dummy *q = 0; |
103 | if (getRandom()) { |
104 | takesNullable(q); |
105 | takesNonnull(q); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} |
106 | } |
107 | Dummy a; |
108 | Dummy *_Nonnull nonnull = &a; |
109 | nonnull = q; // expected-warning {{Null assigned to a pointer which is expected to have non-null value}} |
110 | q = &a; |
111 | takesNullable(q); |
112 | takesNonnull(q); |
113 | } |
114 | |
115 | void testMultiParamChecking(Dummy *_Nonnull a, Dummy *_Nullable b, |
116 | Dummy *_Nonnull c); |
117 | |
118 | void testArgumentTracking(Dummy *_Nonnull nonnull, Dummy *_Nullable nullable) { |
119 | Dummy *p = nullable; |
120 | Dummy *q = nonnull; |
121 | switch(getRandom()) { |
122 | case 1: nonnull = p; break; // expected-warning {{Nullable pointer is assigned to a pointer which is expected to have non-null value}} |
123 | case 2: p = 0; break; |
124 | case 3: q = p; break; |
125 | case 4: testMultiParamChecking(nonnull, nullable, nonnull); break; |
126 | case 5: testMultiParamChecking(nonnull, nonnull, nonnull); break; |
127 | case 6: testMultiParamChecking(nonnull, nullable, nullable); break; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 3rd parameter}} |
128 | case 7: testMultiParamChecking(nullable, nullable, nonnull); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} |
129 | case 8: testMultiParamChecking(nullable, nullable, nullable); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} |
130 | case 9: testMultiParamChecking((Dummy *_Nonnull)0, nullable, nonnull); break; |
131 | } |
132 | } |
133 | |
134 | Dummy *_Nonnull testNullableReturn(Dummy *_Nullable a) { |
135 | Dummy *p = a; |
136 | return p; // expected-warning {{Nullable pointer is returned from a function that is expected to return a non-null value}} |
137 | } |
138 | |
139 | Dummy *_Nonnull testNullReturn() { |
140 | Dummy *p = 0; |
141 | return p; // expected-warning {{Null returned from a function that is expected to return a non-null value}} |
142 | } |
143 | |
144 | void testObjCMessageResultNullability() { |
145 | // The expected result: the most nullable of self and method return type. |
146 | TestObject *o = getUnspecifiedTestObject(); |
147 | int *shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNonnull]; |
148 | switch (getRandom()) { |
149 | case 0: |
150 | // The core analyzer assumes that the receiver is non-null after a message |
151 | // send. This is to avoid some false positives, and increase performance |
152 | // but it also reduces the coverage and makes this checker unable to reason |
153 | // about the nullness of the receiver. |
154 | [o takesNonnull:shouldBeNullable]; // No warning expected. |
155 | break; |
156 | case 1: |
157 | shouldBeNullable = |
158 | [eraseNullab(getNullableTestObject()) returnsUnspecified]; |
159 | [o takesNonnull:shouldBeNullable]; // No warning expected. |
160 | break; |
161 | case 3: |
162 | shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNullable]; |
163 | [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} |
164 | break; |
165 | case 4: |
166 | shouldBeNullable = [eraseNullab(getNonnullTestObject()) returnsNullable]; |
167 | [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} |
168 | break; |
169 | case 5: |
170 | shouldBeNullable = |
171 | [eraseNullab(getUnspecifiedTestObject()) returnsNullable]; |
172 | [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} |
173 | break; |
174 | case 6: |
175 | shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNullable]; |
176 | [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} |
177 | break; |
178 | case 7: { |
179 | int *shouldBeNonnull = [eraseNullab(getNonnullTestObject()) returnsNonnull]; |
180 | [o takesNonnull:shouldBeNonnull]; |
181 | } break; |
182 | } |
183 | } |
184 | |
185 | Dummy * _Nonnull testDirectCastNullableToNonnull() { |
186 | Dummy *p = returnsNullable(); |
187 | takesNonnull((Dummy * _Nonnull)p); // no-warning |
188 | return (Dummy * _Nonnull)p; // no-warning |
189 | } |
190 | |
191 | Dummy * _Nonnull testIndirectCastNullableToNonnull() { |
192 | Dummy *p = (Dummy * _Nonnull)returnsNullable(); |
193 | takesNonnull(p); // no-warning |
194 | return p; // no-warning |
195 | } |
196 | |
197 | Dummy * _Nonnull testDirectCastNilToNonnull() { |
198 | takesNonnull((Dummy * _Nonnull)0); // no-warning |
199 | return (Dummy * _Nonnull)0; // no-warning |
200 | } |
201 | |
202 | void testIndirectCastNilToNonnullAndPass() { |
203 | Dummy *p = (Dummy * _Nonnull)0; |
204 | // FIXME: Ideally the cast above would suppress this warning. |
205 | takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} |
206 | } |
207 | |
208 | void testDirectCastNilToNonnullAndAssignToLocalInInitializer() { |
209 | Dummy * _Nonnull nonnullLocalWithAssignmentInInitializer = (Dummy * _Nonnull)0; // no-warning |
210 | (void)nonnullLocalWithAssignmentInInitializer; |
211 | |
212 | // Since we've already had an invariant violation along this path, |
213 | // we shouldn't warn here. |
214 | nonnullLocalWithAssignmentInInitializer = 0; |
215 | (void)nonnullLocalWithAssignmentInInitializer; |
216 | |
217 | } |
218 | |
219 | void testDirectCastNilToNonnullAndAssignToLocal(Dummy * _Nonnull p) { |
220 | Dummy * _Nonnull nonnullLocalWithAssignment = p; |
221 | nonnullLocalWithAssignment = (Dummy * _Nonnull)0; // no-warning |
222 | (void)nonnullLocalWithAssignment; |
223 | |
224 | // Since we've already had an invariant violation along this path, |
225 | // we shouldn't warn here. |
226 | nonnullLocalWithAssignment = 0; |
227 | (void)nonnullLocalWithAssignment; |
228 | } |
229 | |
230 | void testDirectCastNilToNonnullAndAssignToParam(Dummy * _Nonnull p) { |
231 | p = (Dummy * _Nonnull)0; // no-warning |
232 | } |
233 | |
234 | @interface ClassWithNonnullIvar : NSObject { |
235 | Dummy *_nonnullIvar; |
236 | } |
237 | @end |
238 | |
239 | @implementation ClassWithNonnullIvar |
240 | -(void)testDirectCastNilToNonnullAndAssignToIvar { |
241 | _nonnullIvar = (Dummy * _Nonnull)0; // no-warning; |
242 | |
243 | // Since we've already had an invariant violation along this path, |
244 | // we shouldn't warn here. |
245 | _nonnullIvar = 0; |
246 | } |
247 | @end |
248 | |
249 | void testIndirectNilPassToNonnull() { |
250 | Dummy *p = 0; |
251 | takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} |
252 | } |
253 | |
254 | void testConditionalNilPassToNonnull(Dummy *p) { |
255 | if (!p) { |
256 | takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} |
257 | } |
258 | } |
259 | |
260 | Dummy * _Nonnull testIndirectCastNilToNonnullAndReturn() { |
261 | Dummy *p = (Dummy * _Nonnull)0; |
262 | // FIXME: Ideally the cast above would suppress this warning. |
263 | return p; // expected-warning {{Null returned from a function that is expected to return a non-null value}} |
264 | } |
265 | |
266 | void testInvalidPropagation() { |
267 | Dummy *p = returnsUnspecified(); |
268 | takesNullable(p); |
269 | takesNonnull(p); |
270 | } |
271 | |
272 | void onlyReportFirstPreconditionViolationOnPath() { |
273 | Dummy *p = returnsNullable(); |
274 | takesNonnull(p); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} |
275 | takesNonnull(p); // No warning. |
276 | // The first warning was not a sink. The analysis expected to continue. |
277 | int i = 0; |
278 | i = 5 / i; // expected-warning {{Division by zero}} |
279 | (void)i; |
280 | } |
281 | |
282 | Dummy *_Nonnull doNotWarnWhenPreconditionIsViolatedInTopFunc( |
283 | Dummy *_Nonnull p) { |
284 | if (!p) { |
285 | Dummy *ret = |
286 | 0; // avoid compiler warning (which is not generated by the analyzer) |
287 | if (getRandom()) |
288 | return ret; // no warning |
289 | else |
290 | return p; // no warning |
291 | } else { |
292 | return p; |
293 | } |
294 | } |
295 | |
296 | Dummy *_Nonnull doNotWarnWhenPreconditionIsViolated(Dummy *_Nonnull p) { |
297 | if (!p) { |
298 | Dummy *ret = |
299 | 0; // avoid compiler warning (which is not generated by the analyzer) |
300 | if (getRandom()) |
301 | return ret; // no warning |
302 | else |
303 | return p; // no warning |
304 | } else { |
305 | return p; |
306 | } |
307 | } |
308 | |
309 | void testPreconditionViolationInInlinedFunction(Dummy *p) { |
310 | doNotWarnWhenPreconditionIsViolated(p); |
311 | } |
312 | |
313 | @interface TestInlinedPreconditionViolationClass : NSObject |
314 | @end |
315 | |
316 | @implementation TestInlinedPreconditionViolationClass |
317 | -(Dummy * _Nonnull) calleeWithParam:(Dummy * _Nonnull) p2 { |
318 | Dummy *x = 0; |
319 | if (!p2) // p2 binding becomes dead at this point. |
320 | return x; // no-warning |
321 | else |
322 | return p2; |
323 | } |
324 | |
325 | -(Dummy *)callerWithParam:(Dummy * _Nonnull) p1 { |
326 | return [self calleeWithParam:p1]; |
327 | } |
328 | |
329 | @end |
330 | |
331 | int * _Nonnull InlinedPreconditionViolationInFunctionCallee(int * _Nonnull p2) { |
332 | int *x = 0; |
333 | if (!p2) // p2 binding becomes dead at this point. |
334 | return x; // no-warning |
335 | else |
336 | return p2; |
337 | } |
338 | |
339 | int * _Nonnull InlinedReturnNullOverSuppressionCallee(int * _Nonnull p2) { |
340 | int *result = 0; |
341 | return result; // no-warning; but this is an over suppression |
342 | } |
343 | |
344 | int *InlinedReturnNullOverSuppressionCaller(int * _Nonnull p1) { |
345 | return InlinedReturnNullOverSuppressionCallee(p1); |
346 | } |
347 | |
348 | void inlinedNullable(Dummy *_Nullable p) { |
349 | if (p) return; |
350 | } |
351 | void inlinedNonnull(Dummy *_Nonnull p) { |
352 | if (p) return; |
353 | } |
354 | void inlinedUnspecified(Dummy *p) { |
355 | if (p) return; |
356 | } |
357 | |
358 | void testNilReturnWithBlock(Dummy *p) { |
359 | p = 0; |
360 | Dummy *_Nonnull (^myblock)(void) = ^Dummy *_Nonnull(void) { |
361 | return p; // TODO: We should warn in blocks. |
362 | }; |
363 | myblock(); |
364 | } |
365 | |
366 | Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) { |
367 | switch (getRandom()) { |
368 | case 1: inlinedNullable(p); break; |
369 | case 2: inlinedNonnull(p); break; |
370 | case 3: inlinedUnspecified(p); break; |
371 | } |
372 | if (getRandom()) |
373 | takesNonnull(p); // no-warning |
374 | |
375 | if (getRandom()) { |
376 | Dummy *_Nonnull varWithInitializer = p; // no-warning |
377 | |
378 | Dummy *_Nonnull var1WithInitializer = p, // no-warning |
379 | *_Nonnull var2WithInitializer = p; // no-warning |
380 | } |
381 | |
382 | if (getRandom()) { |
383 | Dummy *_Nonnull varWithoutInitializer; |
384 | varWithoutInitializer = p; // no-warning |
385 | } |
386 | |
387 | return p; |
388 | } |
389 | |
390 | |
391 | @interface SomeClass : NSObject { |
392 | int instanceVar; |
393 | } |
394 | @end |
395 | |
396 | @implementation SomeClass (MethodReturn) |
397 | - (id)initWithSomething:(int)i { |
398 | if (self = [super init]) { |
399 | instanceVar = i; |
400 | } |
401 | |
402 | return self; |
403 | } |
404 | |
405 | - (TestObject * _Nonnull)testReturnsNullableInNonnullIndirectly { |
406 | TestObject *local = getNullableTestObject(); |
407 | return local; // expected-warning {{Nullable pointer is returned from a method that is expected to return a non-null value}} |
408 | } |
409 | |
410 | - (TestObject * _Nonnull)testReturnsCastSuppressedNullableInNonnullIndirectly { |
411 | TestObject *local = getNullableTestObject(); |
412 | return (TestObject * _Nonnull)local; // no-warning |
413 | } |
414 | |
415 | - (TestObject * _Nonnull)testReturnsNullableInNonnullWhenPreconditionViolated:(TestObject * _Nonnull) p { |
416 | TestObject *local = getNullableTestObject(); |
417 | if (!p) // Pre-condition violated here. |
418 | return local; // no-warning |
419 | else |
420 | return p; // no-warning |
421 | } |
422 | @end |
423 | |
424 | @interface ClassWithInitializers : NSObject |
425 | @end |
426 | |
427 | @implementation ClassWithInitializers |
428 | - (instancetype _Nonnull)initWithNonnullReturnAndSelfCheckingIdiom { |
429 | // This defensive check is a common-enough idiom that we filter don't want |
430 | // to issue a diagnostic for it, |
431 | if (self = [super init]) { |
432 | } |
433 | |
434 | return self; // no-warning |
435 | } |
436 | |
437 | - (instancetype _Nonnull)initWithNonnullReturnAndNilReturnViaLocal { |
438 | self = [super init]; |
439 | // This leaks, but we're not checking for that here. |
440 | |
441 | ClassWithInitializers *other = nil; |
442 | // False negative. Once we have more subtle suppression of defensive checks in |
443 | // initializers we should warn here. |
444 | return other; |
445 | } |
446 | @end |
447 | |
448 | @interface SubClassWithInitializers : ClassWithInitializers |
449 | @end |
450 | |
451 | @implementation SubClassWithInitializers |
452 | // Note: Because this is overriding |
453 | // -[ClassWithInitializers initWithNonnullReturnAndSelfCheckingIdiom], |
454 | // the return type of this method becomes implicitly id _Nonnull. |
455 | - (id)initWithNonnullReturnAndSelfCheckingIdiom { |
456 | if (self = [super initWithNonnullReturnAndSelfCheckingIdiom]) { |
457 | } |
458 | |
459 | return self; // no-warning |
460 | } |
461 | |
462 | - (id _Nonnull)initWithNonnullReturnAndSelfCheckingIdiomV2; { |
463 | // Another common return-checking idiom |
464 | self = [super initWithNonnullReturnAndSelfCheckingIdiom]; |
465 | if (!self) { |
466 | return nil; // no-warning |
467 | } |
468 | |
469 | return self; |
470 | } |
471 | @end |
472 | |
473 | @interface ClassWithCopyWithZone : NSObject<NSCopying,NSMutableCopying> { |
474 | id i; |
475 | } |
476 | |
477 | @end |
478 | |
479 | @implementation ClassWithCopyWithZone |
480 | -(id)copyWithZone:(NSZone *)zone { |
481 | ClassWithCopyWithZone *newInstance = [[ClassWithCopyWithZone alloc] init]; |
482 | if (!newInstance) |
483 | return nil; |
484 | |
485 | newInstance->i = i; |
486 | return newInstance; |
487 | } |
488 | |
489 | -(id)mutableCopyWithZone:(NSZone *)zone { |
490 | ClassWithCopyWithZone *newInstance = [[ClassWithCopyWithZone alloc] init]; |
491 | if (newInstance) { |
492 | newInstance->i = i; |
493 | } |
494 | |
495 | return newInstance; |
496 | } |
497 | @end |
498 | |
499 | NSString * _Nullable returnsNullableString(); |
500 | |
501 | void callFunctionInSystemHeader() { |
502 | NSString *s = returnsNullableString(); |
503 | |
504 | NSSystemFunctionTakingNonnull(s); |
505 | #if !NOSYSTEMHEADERS |
506 | // expected-warning@-2{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} |
507 | #endif |
508 | } |
509 | |
510 | void callMethodInSystemHeader() { |
511 | NSString *s = returnsNullableString(); |
512 | |
513 | NSSystemClass *sc = [[NSSystemClass alloc] init]; |
514 | [sc takesNonnull:s]; |
515 | #if !NOSYSTEMHEADERS |
516 | // expected-warning@-2{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} |
517 | #endif |
518 | } |
519 | |
520 | // Test to make sure the analyzer doesn't warn when an a nullability invariant |
521 | // has already been found to be violated on an instance variable. |
522 | |
523 | @class MyInternalClass; |
524 | @interface MyClass : NSObject { |
525 | MyInternalClass * _Nonnull _internal; |
526 | } |
527 | @end |
528 | |
529 | @interface MyInternalClass : NSObject { |
530 | @public |
531 | id _someIvar; |
532 | } |
533 | -(id _Nonnull)methodWithInternalImplementation; |
534 | @end |
535 | |
536 | @interface MyClass () { |
537 | MyInternalClass * _Nonnull _nilledOutInternal; |
538 | } |
539 | @end |
540 | |
541 | @implementation MyClass |
542 | -(id _Nonnull)methodWithInternalImplementation { |
543 | if (!_internal) |
544 | return nil; // no-warning |
545 | |
546 | return [_internal methodWithInternalImplementation]; |
547 | } |
548 | |
549 | - (id _Nonnull)methodReturningIvarInImplementation; { |
550 | return _internal == 0 ? nil : _internal->_someIvar; // no-warning |
551 | } |
552 | |
553 | -(id _Nonnull)methodWithNilledOutInternal { |
554 | _nilledOutInternal = (id _Nonnull)nil; |
555 | |
556 | return nil; // no-warning |
557 | } |
558 | @end |
559 | |