diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1210,7 +1210,6 @@ switch (S->getStmtClass()) { // C++, OpenMP and ARC stuff we don't support yet. - case Expr::ObjCIndirectCopyRestoreExprClass: case Stmt::CXXDependentScopeMemberExprClass: case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: @@ -1870,6 +1869,21 @@ Bldr.addNodes(Dst); break; } + + case Expr::ObjCIndirectCopyRestoreExprClass: { + // ObjCIndirectCopyRestoreExpr implies passing a temporary for + // correctness of lifetime management. Due to limited analysis + // of ARC, this is implemented as direct arg passing. + Bldr.takeNodes(Pred); + ProgramStateRef state = Pred->getState(); + const auto *OIE = cast(S); + const Expr *E = OIE->getSubExpr(); + SVal V = state->getSVal(E, Pred->getLocationContext()); + Bldr.generateNode(S, Pred, + state->BindExpr(S, Pred->getLocationContext(), V)); + Bldr.addNodes(Dst); + break; + } } } diff --git a/clang/test/Analysis/objc-indirect-copy-restore.m b/clang/test/Analysis/objc-indirect-copy-restore.m new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/objc-indirect-copy-restore.m @@ -0,0 +1,67 @@ +// RUN: %clang_analyze_cc1 -fobjc-arc -analyzer-checker=core,debug.ExprInspection -verify %s + +void clang_analyzer_eval(int); +void clang_analyzer_warnIfReached(); + +extern void __assert_fail (__const char *__assertion, __const char *__file, + unsigned int __line, __const char *__function) + __attribute__ ((__noreturn__)); + +#define assert(expr) \ + ((expr) ? (void)(0) : __assert_fail (#expr, __FILE__, __LINE__, __func__)) + + +@protocol NSObject ++ (nonnull instancetype)alloc; +- (nonnull instancetype)init; +@end +@interface NSObject {} +@end + +@interface NSError : NSObject { +@public + int x; +} +@end + + +@interface SomeClass : NSObject ++ (int)doSomethingWithError:(NSError *__autoreleasing *)error; +@end + +@implementation SomeClass ++ (int)doSomethingWithError:(NSError *__autoreleasing *)error { + if (error) { + NSError *e = [[NSError alloc] init]; + assert(e); + e->x = 5; + *error = e; + clang_analyzer_eval(*error != 0); // expected-warning{{TRUE}} + } + return 0; +} +@end + +void testStrongOutParam(void) { + NSError *error; + clang_analyzer_eval(error != 0); // expected-warning{{FALSE}} + int ok = [SomeClass doSomethingWithError:&error]; + clang_analyzer_eval(ok); // expected-warning{{FALSE}} + clang_analyzer_eval(error != 0); // expected-warning{{TRUE}} + clang_analyzer_eval(error->x == 5); // expected-warning{{TRUE}} +} + +void testAutoreleasingOutParam(void) { + NSError *__autoreleasing error; + clang_analyzer_eval(error != 0); // expected-warning{{FALSE}} + int ok = [SomeClass doSomethingWithError:&error]; + clang_analyzer_eval(ok); // expected-warning{{FALSE}} + clang_analyzer_eval(error != 0); // expected-warning{{TRUE}} + clang_analyzer_eval(error->x == 5); // expected-warning{{TRUE}} +} + +void testNilOutParam(void) { + int ok = [SomeClass doSomethingWithError:(void *)0]; + clang_analyzer_eval(ok); // expected-warning{{FALSE}} +} +