diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2011-06-12 15:46:16 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2011-06-12 15:46:16 +0000 | 
| commit | 29cafa66ad3878dbb9f82615f19fa0bded2e443c (patch) | |
| tree | c5e9e10bc189de0058aa763c47b9920a8351b7df /lib/StaticAnalyzer/Core/CFRefCount.cpp | |
| parent | 01af97d3b23bded2b2b21af19bbc6e4cce49e5b3 (diff) | |
Notes
Diffstat (limited to 'lib/StaticAnalyzer/Core/CFRefCount.cpp')
| -rw-r--r-- | lib/StaticAnalyzer/Core/CFRefCount.cpp | 251 | 
1 files changed, 166 insertions, 85 deletions
diff --git a/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Core/CFRefCount.cpp index d9b1ce825cea..0512e2f08d04 100644 --- a/lib/StaticAnalyzer/Core/CFRefCount.cpp +++ b/lib/StaticAnalyzer/Core/CFRefCount.cpp @@ -930,6 +930,13 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) {        S = getPersistentStopSummary();        break;      } +    // For C++ methods, generate an implicit "stop" summary as well.  We +    // can relax this once we have a clear policy for C++ methods and +    // ownership attributes. +    if (isa<CXXMethodDecl>(FD)) { +      S = getPersistentStopSummary(); +      break; +    }      // [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the      // function's type. @@ -1111,15 +1118,11 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) {  RetainSummary*  RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD,                                                  StringRef FName) { -    if (FName.find("Create") != StringRef::npos ||        FName.find("Copy") != StringRef::npos)      return getCFSummaryCreateRule(FD); -  if (FName.find("Get") != StringRef::npos) -    return getCFSummaryGetRule(FD); - -  return getDefaultSummary(); +  return getCFSummaryGetRule(FD);  }  RetainSummary* @@ -1233,6 +1236,9 @@ RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ,      if (FD->getAttr<CFReturnsRetainedAttr>()) {        Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));      } +    else if (FD->getAttr<CFReturnsNotRetainedAttr>()) { +      Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); +    }    }  } @@ -1758,6 +1764,15 @@ public:                            StmtNodeBuilder& Builder,                            const ReturnStmt* S,                            ExplodedNode* Pred); +   +  void evalReturnWithRetEffect(ExplodedNodeSet& Dst, +                               ExprEngine& Engine, +                               StmtNodeBuilder& Builder, +                               const ReturnStmt* S, +                               ExplodedNode* Pred, +                               RetEffect RE, RefVal X, +                               SymbolRef Sym, const GRState *state); +    // Assumptions. @@ -2075,7 +2090,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,      }      if (CurrV.isOwned()) { -      os << "+1 retain count (owning reference)."; +      os << "+1 retain count";        if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) {          assert(CurrV.getObjKind() == RetEffect::CF); @@ -2085,7 +2100,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,      }      else {        assert (CurrV.isNotOwned()); -      os << "+0 retain count (non-owning reference)."; +      os << "+0 retain count";      }      PathDiagnosticLocation Pos(S, BRC.getSourceManager()); @@ -2217,11 +2232,11 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,          case RefVal::ReturnedOwned:            os << "Object returned to caller as an owning reference (single retain " -          "count transferred to caller)."; +          "count transferred to caller)";            break;          case RefVal::ReturnedNotOwned: -          os << "Object returned to caller with a +0 (non-owning) retain count."; +          os << "Object returned to caller with a +0 retain count";            break;          default: @@ -2354,12 +2369,7 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,    llvm::tie(AllocNode, FirstBinding) =      GetAllocationSite(BRC.getStateManager(), EndN, Sym); -  // Get the allocate site. -  assert(AllocNode); -  const Stmt* FirstStmt = cast<PostStmt>(AllocNode->getLocation()).getStmt(); -    SourceManager& SMgr = BRC.getSourceManager(); -  unsigned AllocLine =SMgr.getInstantiationLineNumber(FirstStmt->getLocStart());    // Compute an actual location for the leak.  Sometimes a leak doesn't    // occur at an actual statement (e.g., transition between blocks; end @@ -2392,10 +2402,14 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,    std::string sbuf;    llvm::raw_string_ostream os(sbuf); -  os << "Object allocated on line " << AllocLine; +  os << "Object leaked: "; -  if (FirstBinding) -    os << " and stored into '" << FirstBinding->getString() << '\''; +  if (FirstBinding) { +    os << "object allocated and stored into '" +       << FirstBinding->getString() << '\''; +  } +  else +    os << "allocated object";    // Get the retain count.    const RefVal* RV = EndN->getState()->get<RefBindings>(Sym); @@ -2404,12 +2418,22 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,      // FIXME: Per comments in rdar://6320065, "create" only applies to CF      // ojbects.  Only "copy", "alloc", "retain" and "new" transfer ownership      // to the caller for NS objects. -    ObjCMethodDecl& MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); -    os << " is returned from a method whose name ('" -       << MD.getSelector().getAsString() -    << "') does not contain 'copy' or otherwise starts with" -    " 'new' or 'alloc'.  This violates the naming convention rules given" -    " in the Memory Management Guide for Cocoa (object leaked)"; +    const Decl *D = &EndN->getCodeDecl(); +    if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { +      os << " is returned from a method whose name ('" +         << MD->getSelector().getAsString() +         << "') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'." +            "  This violates the naming convention rules " +            " given in the Memory Management Guide for Cocoa"; +    } +    else { +      const FunctionDecl *FD = cast<FunctionDecl>(D); +      os << " is return from a function whose name ('" +         << FD->getNameAsString() +         << "') does not contain 'Copy' or 'Create'.  This violates the naming" +            " convention rules given the Memory Management Guide for Core " +            " Foundation"; +    }        }    else if (RV->getKind() == RefVal::ErrorGCLeakReturned) {      ObjCMethodDecl& MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); @@ -2421,7 +2445,7 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,    }    else      os << " is not referenced later in this execution path and has a retain " -          "count of +" << RV->getCount() << " (object leaked)"; +          "count of +" << RV->getCount();    return new PathDiagnosticEventPiece(L, os.str());  } @@ -2494,6 +2518,23 @@ static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) {    return RetTy;  } + +// HACK: Symbols that have ref-count state that are referenced directly +//  (not as structure or array elements, or via bindings) by an argument +//  should not have their ref-count state stripped after we have +//  done an invalidation pass. +// +// FIXME: This is a global to currently share between CFRefCount and +// RetainReleaseChecker.  Eventually all functionality in CFRefCount should +// be migrated to RetainReleaseChecker, and we can make this a non-global. +llvm::DenseSet<SymbolRef> WhitelistedSymbols; +namespace { +struct ResetWhiteList { +  ResetWhiteList() {} +  ~ResetWhiteList() { WhitelistedSymbols.clear(); }  +}; +} +  void CFRefCount::evalSummary(ExplodedNodeSet& Dst,                               ExprEngine& Eng,                               StmtNodeBuilder& Builder, @@ -2510,12 +2551,9 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst,    SymbolRef ErrorSym = 0;    llvm::SmallVector<const MemRegion*, 10> RegionsToInvalidate; - -  // HACK: Symbols that have ref-count state that are referenced directly -  //  (not as structure or array elements, or via bindings) by an argument -  //  should not have their ref-count state stripped after we have -  //  done an invalidation pass. -  llvm::DenseSet<SymbolRef> WhitelistedSymbols; +   +  // Use RAII to make sure the whitelist is properly cleared. +  ResetWhiteList resetWhiteList;    // Invalidate all instance variables of the receiver of a message.    // FIXME: We should be able to do better with inter-procedural analysis. @@ -2624,20 +2662,14 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst,    // NOTE: Even if RegionsToInvalidate is empty, we must still invalidate    //  global variables. -  state = state->invalidateRegions(RegionsToInvalidate.data(), -                                   RegionsToInvalidate.data() + -                                   RegionsToInvalidate.size(), -                                   Ex, Count, &IS, -                                   /* invalidateGlobals = */ true); - -  for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(), -       E = IS.end(); I!=E; ++I) { -    SymbolRef sym = *I; -    if (WhitelistedSymbols.count(sym)) -      continue; -    // Remove any existing reference-count binding. -    state = state->remove<RefBindings>(*I); -  } +  // NOTE: RetainReleaseChecker handles the actual invalidation of symbols. +  state = +    state->invalidateRegions(RegionsToInvalidate.data(), +                             RegionsToInvalidate.data() + +                             RegionsToInvalidate.size(), +                             Ex, Count, &IS, +                             /* invalidateGlobals = */ +                             Eng.doesInvalidateGlobals(callOrMsg));    // Evaluate the effect on the message receiver.    if (!ErrorRange.isValid() && Receiver) { @@ -2946,30 +2978,50 @@ void CFRefCount::evalReturn(ExplodedNodeSet& Dst,    assert(T);    X = *T; +  // Consult the summary of the enclosing method. +  Decl const *CD = &Pred->getCodeDecl(); + +  if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) { +    const RetainSummary &Summ = *Summaries.getMethodSummary(MD); +    return evalReturnWithRetEffect(Dst, Eng, Builder, S,  +                                   Pred, Summ.getRetEffect(), X, +                                   Sym, state); +  } + +  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) { +    if (!isa<CXXMethodDecl>(FD)) +      if (const RetainSummary *Summ = Summaries.getSummary(FD)) +        return evalReturnWithRetEffect(Dst, Eng, Builder, S,  +                                       Pred, Summ->getRetEffect(), X, +                                       Sym, state); +  } +} + +void CFRefCount::evalReturnWithRetEffect(ExplodedNodeSet &Dst,  +                                         ExprEngine &Eng, +                                         StmtNodeBuilder &Builder, +                                         const ReturnStmt *S, +                                         ExplodedNode *Pred, +                                         RetEffect RE, RefVal X, +                                         SymbolRef Sym, const GRState *state) {    // Any leaks or other errors?    if (X.isReturnedOwned() && X.getCount() == 0) { -    Decl const *CD = &Pred->getCodeDecl(); -    if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) { -      const RetainSummary &Summ = *Summaries.getMethodSummary(MD); -      RetEffect RE = Summ.getRetEffect(); +    if (RE.getKind() != RetEffect::NoRet) {        bool hasError = false; - -      if (RE.getKind() != RetEffect::NoRet) { -        if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { -          // Things are more complicated with garbage collection.  If the -          // returned object is suppose to be an Objective-C object, we have -          // a leak (as the caller expects a GC'ed object) because no -          // method should return ownership unless it returns a CF object. -          hasError = true; -          X = X ^ RefVal::ErrorGCLeakReturned; -        } -        else if (!RE.isOwned()) { -          // Either we are using GC and the returned object is a CF type -          // or we aren't using GC.  In either case, we expect that the -          // enclosing method is expected to return ownership. -          hasError = true; -          X = X ^ RefVal::ErrorLeakReturned; -        } +      if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { +        // Things are more complicated with garbage collection.  If the +        // returned object is suppose to be an Objective-C object, we have +        // a leak (as the caller expects a GC'ed object) because no +        // method should return ownership unless it returns a CF object. +        hasError = true; +        X = X ^ RefVal::ErrorGCLeakReturned; +      } +      else if (!RE.isOwned()) { +        // Either we are using GC and the returned object is a CF type +        // or we aren't using GC.  In either case, we expect that the +        // enclosing method is expected to return ownership. +        hasError = true; +        X = X ^ RefVal::ErrorLeakReturned;        }        if (hasError) { @@ -2987,26 +3039,24 @@ void CFRefCount::evalReturn(ExplodedNodeSet& Dst,          }        }      } +    return;    } -  else if (X.isReturnedNotOwned()) { -    Decl const *CD = &Pred->getCodeDecl(); -    if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) { -      const RetainSummary &Summ = *Summaries.getMethodSummary(MD); -      if (Summ.getRetEffect().isOwned()) { -        // Trying to return a not owned object to a caller expecting an -        // owned object. - -        static int ReturnNotOwnedForOwnedTag = 0; -        state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned); -        if (ExplodedNode *N = -            Builder.generateNode(PostStmt(S, Pred->getLocationContext(), -                                          &ReturnNotOwnedForOwnedTag), -                                 state, Pred)) { -            CFRefReport *report = -                new CFRefReport(*static_cast<CFRefBug*>(returnNotOwnedForOwned), -                                *this, N, Sym); -            BR->EmitReport(report); -        } + +  if (X.isReturnedNotOwned()) { +    if (RE.isOwned()) { +      // Trying to return a not owned object to a caller expecting an +      // owned object. + +      static int ReturnNotOwnedForOwnedTag = 0; +      state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned); +      if (ExplodedNode *N = +          Builder.generateNode(PostStmt(S, Pred->getLocationContext(), +                                        &ReturnNotOwnedForOwnedTag), +                               state, Pred)) { +          CFRefReport *report = +              new CFRefReport(*static_cast<CFRefBug*>(returnNotOwnedForOwned), +                              *this, N, Sym); +          BR->EmitReport(report);        }      }    } @@ -3418,12 +3468,43 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst,  namespace {  class RetainReleaseChecker -  : public Checker< check::PostStmt<BlockExpr> > { +  : public Checker< check::PostStmt<BlockExpr>, check::RegionChanges > {  public: +    bool wantsRegionUpdate; +     +    RetainReleaseChecker() : wantsRegionUpdate(true) {} +     +          void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; +    const GRState *checkRegionChanges(const GRState *state, +                            const StoreManager::InvalidatedSymbols *invalidated, +                                      const MemRegion * const *begin, +                                      const MemRegion * const *end) const; +                                           +    bool wantsRegionChangeUpdate(const GRState *state) const { +      return wantsRegionUpdate; +    }  };  } // end anonymous namespace +const GRState * +RetainReleaseChecker::checkRegionChanges(const GRState *state, +                            const StoreManager::InvalidatedSymbols *invalidated, +                                         const MemRegion * const *begin, +                                         const MemRegion * const *end) const { +  if (!invalidated) +    return state; + +  for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), +       E = invalidated->end(); I!=E; ++I) { +    SymbolRef sym = *I; +    if (WhitelistedSymbols.count(sym)) +      continue; +    // Remove any existing reference-count binding. +    state = state->remove<RefBindings>(sym); +  } +  return state; +}  void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE,                                           CheckerContext &C) const {  | 
