diff options
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 16 | ||||
-rw-r--r-- | test/Analysis/inlining/InlineObjCInstanceMethod.m | 39 |
2 files changed, 51 insertions, 4 deletions
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 5b88a45495..2e47a9a61c 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -191,6 +191,15 @@ void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC, currBldrCtx = 0; } +static bool isDifferentDeclUsedAtRuntime(CallEventRef<> Call, + const StackFrameContext *calleeCtx) { + const Decl *RuntimeCallee = calleeCtx->getDecl(); + const Decl *StaticDecl = Call->getDecl(); + if (!RuntimeCallee || !StaticDecl) + return false; + return RuntimeCallee->getCanonicalDecl() != StaticDecl->getCanonicalDecl(); +} + /// The call exit is simulated with a sequence of nodes, which occur between /// CallExitBegin and CallExitEnd. The following operations occur between the /// two program points: @@ -230,9 +239,10 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { const LocationContext *LCtx = CEBNode->getLocationContext(); SVal V = state->getSVal(RS, LCtx); - const Decl *Callee = calleeCtx->getDecl(); - if (Callee != Call->getDecl()) { - QualType ReturnedTy = CallEvent::getDeclaredResultType(Callee); + // Ensure that the return type matches the type of the returned Expr. + if (isDifferentDeclUsedAtRuntime(Call, calleeCtx)) { + QualType ReturnedTy = + CallEvent::getDeclaredResultType(calleeCtx->getDecl()); if (!ReturnedTy.isNull()) { if (const Expr *Ex = dyn_cast<Expr>(CE)) { V = adjustReturnValue(V, Ex->getType(), ReturnedTy, diff --git a/test/Analysis/inlining/InlineObjCInstanceMethod.m b/test/Analysis/inlining/InlineObjCInstanceMethod.m index 21ce8576a4..f6aa50a248 100644 --- a/test/Analysis/inlining/InlineObjCInstanceMethod.m +++ b/test/Analysis/inlining/InlineObjCInstanceMethod.m @@ -1,15 +1,32 @@ -// RUN: %clang --analyze -Xanalyzer -analyzer-checker=osx.cocoa.IncompatibleMethodTypes -Xclang -verify %s +// RUN: %clang --analyze -Xanalyzer -analyzer-checker=osx.cocoa.IncompatibleMethodTypes,osx.coreFoundation.CFRetainRelease -Xclang -verify %s #include "InlineObjCInstanceMethod.h" +typedef const struct __CFString * CFStringRef; +typedef const void * CFTypeRef; +extern CFTypeRef CFRetain(CFTypeRef cf); +extern void CFRelease(CFTypeRef cf); +extern CFStringRef getString(void); + // Method is defined in the parent; called through self. @interface MyParent : NSObject - (int)getInt; +- (const struct __CFString *) testCovariantReturnType __attribute__((cf_returns_retained)); @end @implementation MyParent - (int)getInt { return 0; } + +- (CFStringRef) testCovariantReturnType __attribute__((cf_returns_retained)) { + CFStringRef Str = ((void*)0); + Str = getString(); + if (Str) { + CFRetain(Str); + } + return Str; +} + @end @interface MyClass : MyParent @@ -88,12 +105,22 @@ void randomlyMessageAnObject(MyClass *arr[], int i) { @interface EvilChild : MyParent - (id)getInt; +- (const struct __CFString *) testCovariantReturnType __attribute__((cf_returns_retained)); @end @implementation EvilChild - (id)getInt { // expected-warning {{types are incompatible}} return self; } +- (CFStringRef) testCovariantReturnType __attribute__((cf_returns_retained)) { + CFStringRef Str = ((void*)0); + Str = getString(); + if (Str) { + CFRetain(Str); + } + return Str; +} + @end int testNonCovariantReturnType() { @@ -109,3 +136,13 @@ int testNonCovariantReturnType() { [obj release]; return 5/(x-1); // no-warning } + +int testCovariantReturnTypeNoErrorSinceTypesMatch() { + MyParent *obj = [[EvilChild alloc] init]; + + CFStringRef S = ((void*)0); + S = [obj testCovariantReturnType]; + if (S) + CFRelease(S); + CFRelease(obj); +} |