From e94192198f8a949c7880620b06e9ef85d87ad4b3 Mon Sep 17 00:00:00 2001 From: Paul Pelzl Date: Wed, 3 Jun 2020 11:39:12 +0300 Subject: [PATCH] [analyzer] Add support for ObjCIndirectCopyRestoreExpr. Idiomatic objc using ARC will generate this expression regularly due to NSError out-param passing. Providing an implementation for this expression allows the analyzer to explore many more codepaths in ARC projects. The current implementation is not perfect but the differences are hopefully subtle enough to not cause much problems. rdar://63918914 Differential Revision: https://reviews.llvm.org/D81071 --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 16 ++++- .../Analysis/objc-indirect-copy-restore.m | 67 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 clang/test/Analysis/objc-indirect-copy-restore.m diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 81caf0f47553..265dcd134213 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1210,7 +1210,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, 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 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, 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 index 000000000000..4881d218f31b --- /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}} +} +