Commit 8a7f8bff authored by Anna Zaks's avatar Anna Zaks
Browse files

[analyzer] Follow up to r167762 - precisely determine the adjustment

conditions.

The adjustment is needed only in case of dynamic dispatch performed by
the analyzer - when the runtime declaration is different from the static
one.

Document this explicitly in the code (by adding a helper). Also, use
canonical Decls to avoid matching against the case where the definition
is different from found declaration.

This fix suppresses the testcase I added in r167762, so add another
testcase to make sure we do test commit r167762.

llvm-svn: 167797
parent b15f39b1
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -191,6 +191,16 @@ void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC,
  currBldrCtx = 0;
}

static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call,
    const StackFrameContext *calleeCtx) {
  const Decl *RuntimeCallee = calleeCtx->getDecl();
  const Decl *StaticDecl = Call->getDecl();
  assert(RuntimeCallee);
  if (!StaticDecl)
    return true;
  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 +240,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 (wasDifferentDeclUsedForInlining(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,
+38 −1
Original line number Diff line number Diff line
// 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);
}