[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
This commit is contained in:
Paul Pelzl 2020-06-03 11:39:12 +03:00 committed by Artem Dergachev
parent 3abe7aca45
commit e94192198f
2 changed files with 82 additions and 1 deletions

View file

@ -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<ObjCIndirectCopyRestoreExpr>(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;
}
}
}

View file

@ -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 <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}}
}