diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
| -rw-r--r-- | lib/StaticAnalyzer/Core/CFRefCount.cpp | 142 | ||||
| -rw-r--r-- | lib/StaticAnalyzer/Core/Environment.cpp | 3 | ||||
| -rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 78 | ||||
| -rw-r--r-- | lib/StaticAnalyzer/Core/RegionStore.cpp | 7 | ||||
| -rw-r--r-- | lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp | 36 | 
5 files changed, 205 insertions, 61 deletions
| diff --git a/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Core/CFRefCount.cpp index 0512e2f08d04..bf5302920819 100644 --- a/lib/StaticAnalyzer/Core/CFRefCount.cpp +++ b/lib/StaticAnalyzer/Core/CFRefCount.cpp @@ -126,6 +126,7 @@ public:  /// ArgEffect is used to summarize a function/method call's effect on a  /// particular argument.  enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing, +                 DecRefBridgedTransfered,                   DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape,                   NewAutoreleasePool, SelfOwn, StopTracking }; @@ -148,7 +149,8 @@ namespace {  class RetEffect {  public:    enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, -              NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias, +              NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol, +              ReceiverAlias,                OwnedWhenTrackedReceiver };    enum ObjKind { CF, ObjC, AnyObj }; @@ -195,7 +197,9 @@ public:    static RetEffect MakeGCNotOwned() {      return RetEffect(GCNotOwnedSymbol, ObjC);    } - +  static RetEffect MakeARCNotOwned() { +    return RetEffect(ARCNotOwnedSymbol, ObjC); +  }    static RetEffect MakeNoRet() {      return RetEffect(NoRet);    } @@ -636,6 +640,9 @@ class RetainSummaryManager {    /// GCEnabled - Records whether or not the analyzed code runs in GC mode.    const bool GCEnabled; +  /// Records whether or not the analyzed code runs in ARC mode. +  const bool ARCEnabled; +    /// FuncSummaries - A map from FunctionDecls to summaries.    FuncSummariesTy FuncSummaries; @@ -788,14 +795,20 @@ private:  public: -  RetainSummaryManager(ASTContext& ctx, bool gcenabled) +  RetainSummaryManager(ASTContext& ctx, bool gcenabled, bool usesARC)     : Ctx(ctx),       CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")), -     GCEnabled(gcenabled), AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), -     ObjCAllocRetE(gcenabled ? RetEffect::MakeGCNotOwned() -                             : RetEffect::MakeOwned(RetEffect::ObjC, true)), -     ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned() -                            : RetEffect::MakeOwnedWhenTrackedReceiver()), +     GCEnabled(gcenabled), +     ARCEnabled(usesARC), +     AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), +     ObjCAllocRetE(gcenabled +                    ? RetEffect::MakeGCNotOwned() +                    : (usesARC ? RetEffect::MakeARCNotOwned() +                               : RetEffect::MakeOwned(RetEffect::ObjC, true))), +     ObjCInitRetE(gcenabled  +                    ? RetEffect::MakeGCNotOwned() +                    : (usesARC ? RetEffect::MakeARCNotOwned() +                               : RetEffect::MakeOwnedWhenTrackedReceiver())),       DefaultSummary(AF.getEmptyMap() /* per-argument effects (none) */,                      RetEffect::MakeNoRet() /* return effect */,                      MayEscape, /* default argument effect */ @@ -871,6 +884,10 @@ public:    bool isGCEnabled() const { return GCEnabled; } +  bool isARCEnabled() const { return ARCEnabled; } +   +  bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; } +    RetainSummary *copySummary(RetainSummary *OldSumm) {      RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();      new (Summ) RetainSummary(*OldSumm); @@ -1118,8 +1135,7 @@ 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) +  if (coreFoundation::followsCreateRule(FName))      return getCFSummaryCreateRule(FD);    return getCFSummaryGetRule(FD); @@ -1189,7 +1205,8 @@ RetainSummaryManager::getInitMethodSummary(QualType RetTy) {    assert(ScratchArgs.isEmpty());    // 'init' methods conceptually return a newly allocated object and claim    // the receiver. -  if (cocoa::isCocoaObjectRef(RetTy) || cocoa::isCFObjectRef(RetTy)) +  if (cocoa::isCocoaObjectRef(RetTy) || +      coreFoundation::isCFObjectRef(RetTy))      return getPersistentSummary(ObjCInitRetE, DecRefMsg);    return getDefaultSummary(); @@ -1332,15 +1349,15 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD,    if (cocoa::isCocoaObjectRef(RetTy)) {      // EXPERIMENTAL: assume the Cocoa conventions for all objects returned      //  by instance methods. -    RetEffect E = cocoa::followsFundamentalRule(S) +    RetEffect E = cocoa::followsFundamentalRule(S, MD)                    ? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC);      return getPersistentSummary(E, ReceiverEff, MayEscape);    }    // Look for methods that return an owned core foundation object. -  if (cocoa::isCFObjectRef(RetTy)) { -    RetEffect E = cocoa::followsFundamentalRule(S) +  if (coreFoundation::isCFObjectRef(RetTy)) { +    RetEffect E = cocoa::followsFundamentalRule(S, MD)        ? RetEffect::MakeOwned(RetEffect::CF, true)        : RetEffect::MakeNotOwned(RetEffect::CF); @@ -1411,7 +1428,7 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S,      assert(ScratchArgs.isEmpty());      // "initXXX": pass-through for receiver. -    if (cocoa::deriveNamingConvention(S) == cocoa::InitRule) +    if (cocoa::deriveNamingConvention(S, MD) == cocoa::InitRule)        Summ = getInitMethodSummary(RetTy);      else        Summ = getCommonMethodSummary(MD, S, RetTy); @@ -1654,7 +1671,6 @@ public:                         const char* nl, const char* sep);    }; -private:    typedef llvm::DenseMap<const ExplodedNode*, const RetainSummary*>      SummaryLogTy; @@ -1691,7 +1707,7 @@ private:  public:    CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts) -    : Summaries(Ctx, gcenabled), +    : Summaries(Ctx, gcenabled, (bool)lopts.ObjCAutoRefCount),        LOpts(lopts), useAfterRelease(0), releaseNotOwned(0),        deallocGC(0), deallocNotOwned(0),        leakWithinFunction(0), leakAtReturn(0), overAutorelease(0), @@ -1706,6 +1722,8 @@ public:    }    bool isGCEnabled() const { return Summaries.isGCEnabled(); } +  bool isARCorGCEnabled() const { return Summaries.isARCorGCEnabled(); } +      const LangOptions& getLangOptions() const { return LOpts; }    const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const { @@ -1907,7 +1925,7 @@ namespace {        CFRefBug(tf, "Method should return an owned object") {}      const char *getDescription() const { -      return "Object with +0 retain counts returned to caller where a +1 " +      return "Object with a +0 retain count returned to caller where a +1 "               "(owning) retain count is expected";      }    }; @@ -2094,7 +2112,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,        if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) {          assert(CurrV.getObjKind() == RetEffect::CF); -        os << "  " +        os << ".  "          "Core Foundation objects are not automatically garbage collected.";        }      } @@ -2416,14 +2434,14 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,    if (RV->getKind() == RefVal::ErrorLeakReturned) {      // FIXME: Per comments in rdar://6320065, "create" only applies to CF -    // ojbects.  Only "copy", "alloc", "retain" and "new" transfer ownership +    // objects.  Only "copy", "alloc", "retain" and "new" transfer ownership      // to the caller for NS objects.      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 " +            "  This violates the naming convention rules"              " given in the Memory Management Guide for Cocoa";      }      else { @@ -2431,7 +2449,7 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,        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 " +            " convention rules given the Memory Management Guide for Core"              " Foundation";      }        } @@ -2777,6 +2795,7 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst,      }      case RetEffect::GCNotOwnedSymbol: +    case RetEffect::ARCNotOwnedSymbol:      case RetEffect::NotOwnedSymbol: {        unsigned Count = Builder.getCurrentBlockCount();        SValBuilder &svalBuilder = Eng.getSValBuilder(); @@ -3103,8 +3122,8 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,    // In GC mode [... release] and [... retain] do nothing.    switch (E) {      default: break; -    case IncRefMsg: E = isGCEnabled() ? DoNothing : IncRef; break; -    case DecRefMsg: E = isGCEnabled() ? DoNothing : DecRef; break; +    case IncRefMsg: E = isARCorGCEnabled() ? DoNothing : IncRef; break; +    case DecRefMsg: E = isARCorGCEnabled() ? DoNothing : DecRef; break;      case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break;      case NewAutoreleasePool: E = isGCEnabled() ? DoNothing :                                                   NewAutoreleasePool; break; @@ -3118,9 +3137,13 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,    }    switch (E) { -    default: -      assert (false && "Unhandled CFRef transition."); - +    case DecRefMsg: +    case IncRefMsg: +    case MakeCollectable: +      assert(false && +             "DecRefMsg/IncRefMsg/MakeCollectable already transformed"); +      return state; +            case Dealloc:        // Any use of -dealloc in GC is *bad*.        if (isGCEnabled()) { @@ -3193,6 +3216,7 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,        V = V ^ RefVal::NotOwned;        // Fall-through.      case DecRef: +    case DecRefBridgedTransfered:        switch (V.getKind()) {          default:            // case 'RefVal::Released' handled above. @@ -3200,7 +3224,9 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,          case RefVal::Owned:            assert(V.getCount() > 0); -          if (V.getCount() == 1) V = V ^ RefVal::Released; +          if (V.getCount() == 1) +            V = V ^ (E == DecRefBridgedTransfered ?  +                      RefVal::NotOwned : RefVal::Released);            V = V - 1;            break; @@ -3280,15 +3306,10 @@ CFRefCount::HandleAutoreleaseCounts(const GRState * state,      std::string sbuf;      llvm::raw_string_ostream os(sbuf); -    os << "Object over-autoreleased: object was sent -autorelease"; +    os << "Object over-autoreleased: object was sent -autorelease ";      if (V.getAutoreleaseCount() > 1) -      os << V.getAutoreleaseCount() << " times"; -    os << " but the object has "; -    if (V.getCount() == 0) -      os << "zero (locally visible)"; -    else -      os << "+" << V.getCount(); -    os << " retain counts"; +      os << V.getAutoreleaseCount() << " times "; +    os << "but the object has a +" << V.getCount() << " retain count";      CFRefReport *report =        new CFRefReport(*static_cast<CFRefBug*>(overAutorelease), @@ -3468,7 +3489,9 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst,  namespace {  class RetainReleaseChecker -  : public Checker< check::PostStmt<BlockExpr>, check::RegionChanges > { +  : public Checker< check::PostStmt<BlockExpr>, +                    check::PostStmt<CastExpr>, +                    check::RegionChanges > {  public:      bool wantsRegionUpdate; @@ -3476,6 +3499,9 @@ public:      void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; +                       +    void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; +      const GRState *checkRegionChanges(const GRState *state,                              const StoreManager::InvalidatedSymbols *invalidated,                                        const MemRegion * const *begin, @@ -3545,6 +3571,48 @@ void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE,    C.addTransition(state);  } +void RetainReleaseChecker::checkPostStmt(const CastExpr *CE, +                                         CheckerContext &C) const { +  const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE); +  if (!BE) +    return; +   +  ArgEffect AE = IncRef; +   +  switch (BE->getBridgeKind()) { +    case clang::OBC_Bridge: +      // Do nothing. +      return; +    case clang::OBC_BridgeRetained: +      AE = IncRef; +      break;       +    case clang::OBC_BridgeTransfer: +      AE = DecRefBridgedTransfered; +      break; +  } +   +  const GRState *state = C.getState(); +  SymbolRef Sym = state->getSVal(CE).getAsLocSymbol(); +  if (!Sym) +    return; +  const RefVal* T = state->get<RefBindings>(Sym); +  if (!T) +    return; + +  // This is gross.  Once the checker and CFRefCount are unified, +  // this will go away. +  CFRefCount &cf = static_cast<CFRefCount&>(C.getEngine().getTF()); +  RefVal::Kind hasErr = (RefVal::Kind) 0; +  state = cf.Update(state, Sym, *T, AE, hasErr); +   +  if (hasErr) { +     +    return; +  } + +  C.generateNode(state); +} +  //===----------------------------------------------------------------------===//  // Transfer function creation for external clients.  //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index 48f126bfd863..3961c7b95259 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -83,6 +83,9 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder,        case Stmt::CXXBindTemporaryExprClass:          E = cast<CXXBindTemporaryExpr>(E)->getSubExpr();          continue; +      case Stmt::MaterializeTemporaryExprClass: +        E = cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(); +        continue;        // Handle all other Stmt* using a lookup.        default:          break; diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index aed39eb0cec6..ffe5f0b6cdf2 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -442,7 +442,8 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,    }    switch (S->getStmtClass()) { -    // C++ stuff we don't support yet. +    // C++ and ARC stuff we don't support yet. +    case Expr::ObjCIndirectCopyRestoreExprClass:      case Stmt::CXXBindTemporaryExprClass:      case Stmt::CXXCatchStmtClass:      case Stmt::CXXDependentScopeMemberExprClass: @@ -478,6 +479,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,      // We don't handle default arguments either yet, but we can fake it      // for now by just skipping them. +    case Stmt::SubstNonTypeTemplateParmExprClass:      case Stmt::CXXDefaultArgExprClass: {        Dst.Add(Pred);        break; @@ -508,7 +510,10 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,        break;      case Stmt::GNUNullExprClass: { -      MakeNode(Dst, S, Pred, GetState(Pred)->BindExpr(S, svalBuilder.makeNull())); +      // GNU __null is a pointer-width integer, not an actual pointer. +      const GRState *state = GetState(Pred); +      state = state->BindExpr(S, svalBuilder.makeIntValWithPtrWidth(0, false)); +      MakeNode(Dst, S, Pred, state);        break;      } @@ -520,14 +525,27 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,        VisitObjCPropertyRefExpr(cast<ObjCPropertyRefExpr>(S), Pred, Dst);        break; +    case Stmt::ImplicitValueInitExprClass: { +      const GRState *state = GetState(Pred); +      QualType ty = cast<ImplicitValueInitExpr>(S)->getType(); +      SVal val = svalBuilder.makeZeroVal(ty); +      MakeNode(Dst, S, Pred, state->BindExpr(S, val)); +      break; +    } +       +    case Stmt::ExprWithCleanupsClass: { +      Visit(cast<ExprWithCleanups>(S)->getSubExpr(), Pred, Dst); +      break; +    } +      // Cases not handled yet; but will handle some day.      case Stmt::DesignatedInitExprClass:      case Stmt::ExtVectorElementExprClass:      case Stmt::ImaginaryLiteralClass: -    case Stmt::ImplicitValueInitExprClass:      case Stmt::ObjCAtCatchStmtClass:      case Stmt::ObjCAtFinallyStmtClass:      case Stmt::ObjCAtTryStmtClass: +    case Stmt::ObjCAutoreleasePoolStmtClass:      case Stmt::ObjCEncodeExprClass:      case Stmt::ObjCIsaExprClass:      case Stmt::ObjCProtocolExprClass: @@ -548,7 +566,6 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,      case Stmt::IntegerLiteralClass:      case Stmt::CharacterLiteralClass:      case Stmt::CXXBoolLiteralExprClass: -    case Stmt::ExprWithCleanupsClass:      case Stmt::FloatingLiteralClass:      case Stmt::SizeOfPackExprClass:      case Stmt::CXXNullPtrLiteralExprClass: @@ -668,12 +685,35 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,      case Stmt::CXXDynamicCastExprClass:      case Stmt::CXXReinterpretCastExprClass:      case Stmt::CXXConstCastExprClass: -    case Stmt::CXXFunctionalCastExprClass: { +    case Stmt::CXXFunctionalCastExprClass:  +    case Stmt::ObjCBridgedCastExprClass: {        const CastExpr* C = cast<CastExpr>(S); -      VisitCast(C, C->getSubExpr(), Pred, Dst); +      // Handle the previsit checks. +      ExplodedNodeSet dstPrevisit; +      getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this); +       +      // Handle the expression itself. +      ExplodedNodeSet dstExpr; +      for (ExplodedNodeSet::iterator i = dstPrevisit.begin(), +                                     e = dstPrevisit.end(); i != e ; ++i) {  +        VisitCast(C, C->getSubExpr(), *i, dstExpr); +      } + +      // Handle the postvisit checks. +      getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this);        break;      } +    case Expr::MaterializeTemporaryExprClass: { +      const MaterializeTemporaryExpr *Materialize +                                            = cast<MaterializeTemporaryExpr>(S); +      if (!Materialize->getType()->isRecordType()) +        CreateCXXTemporaryObject(Materialize->GetTemporaryExpr(), Pred, Dst); +      else +        Visit(Materialize->GetTemporaryExpr(), Pred, Dst); +      break; +    } +            case Stmt::InitListExprClass:        VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst);        break; @@ -2148,10 +2188,19 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,      Pred = *I;      switch (CastE->getCastKind()) { +      case CK_LValueToRValue: +        assert(false && "LValueToRValue casts handled earlier."); +      case CK_GetObjCProperty: +        assert(false && "GetObjCProperty casts handled earlier.");        case CK_ToVoid:          Dst.Add(Pred);          continue; -      case CK_LValueToRValue: +      // The analyzer doesn't do anything special with these casts, +      // since it understands retain/release semantics already. +      case CK_ObjCProduceObject: +      case CK_ObjCConsumeObject: +      case CK_ObjCReclaimReturnedObject: // Fall-through. +      // True no-ops.        case CK_NoOp:        case CK_FunctionToPointerDecay: {          // Copy the SVal of Ex to CastE. @@ -2161,7 +2210,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,          MakeNode(Dst, CastE, Pred, state);          continue;        } -      case CK_GetObjCProperty:        case CK_Dependent:        case CK_ArrayToPointerDecay:        case CK_BitCast: @@ -2273,17 +2321,9 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,    //  time a function is called those values may not be current.    ExplodedNodeSet Tmp; -  if (InitEx) { -    if (VD->getType()->isReferenceType() && !InitEx->isLValue()) { -      // If the initializer is C++ record type, it should already has a  -      // temp object. -      if (!InitEx->getType()->isRecordType()) -        CreateCXXTemporaryObject(InitEx, Pred, Tmp); -      else -        Tmp.Add(Pred); -    } else -      Visit(InitEx, Pred, Tmp); -  } else +  if (InitEx) +    Visit(InitEx, Pred, Tmp); +  else      Tmp.Add(Pred);    ExplodedNodeSet Tmp2; diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index d0d8f601f071..23dd6416a8f1 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1377,7 +1377,12 @@ StoreRef RegionStoreManager::setImplicitDefaultValue(Store store,      V = svalBuilder.makeZeroVal(Ctx.IntTy);    }    else { -    return StoreRef(store, *this); +    // We can't represent values of this type, but we still need to set a value +    // to record that the region has been initialized. +    // If this assertion ever fires, a new case should be added above -- we +    // should know how to default-initialize any value we can symbolicate. +    assert(!SymbolManager::canSymbolicate(T) && "This type is representable"); +    V = UnknownVal();    }    return StoreRef(addBinding(B, R, BindingKey::Default, diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 197442b1574f..80c18a335fde 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -415,6 +415,24 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,      case nonloc::ConcreteIntKind: {        const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs); +      // Is the RHS a symbol we can simplify? +      // FIXME: This was mostly copy/pasted from the LHS-is-a-symbol case. +      if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { +        SymbolRef RSym = srhs->getSymbol(); +        if (RSym->getType(Context)->isIntegerType()) { +          if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { +            // The symbol evaluates to a constant. +            const llvm::APSInt *rhs_I; +            if (BinaryOperator::isRelationalOp(op)) +              rhs_I = &BasicVals.Convert(lhsInt.getValue(), *Constant); +            else +              rhs_I = &BasicVals.Convert(resultTy, *Constant); + +            rhs = nonloc::ConcreteInt(*rhs_I); +          } +        } +      } +        if (isa<nonloc::ConcreteInt>(rhs)) {          return lhsInt.evalBinOp(*this, op, cast<nonloc::ConcreteInt>(rhs));        } else { @@ -461,13 +479,22 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,      case nonloc::SymbolValKind: {        nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs);        SymbolRef Sym = slhs->getSymbol(); +      QualType lhsType = Sym->getType(Context); + +      // The conversion type is usually the result type, but not in the case +      // of relational expressions. +      QualType conversionType = resultTy; +      if (BinaryOperator::isRelationalOp(op)) +        conversionType = lhsType; +        // Does the symbol simplify to a constant?  If so, "fold" the constant        // by setting 'lhs' to a ConcreteInt and try again. -      if (Sym->getType(Context)->isIntegerType()) +      if (lhsType->isIntegerType())          if (const llvm::APSInt *Constant = state->getSymVal(Sym)) {            // The symbol evaluates to a constant. If necessary, promote the            // folded constant (LHS) to the result type. -          const llvm::APSInt &lhs_I = BasicVals.Convert(resultTy, *Constant); +          const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType, +                                                        *Constant);            lhs = nonloc::ConcreteInt(lhs_I);            // Also promote the RHS (if necessary). @@ -479,7 +506,7 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,            // Other operators: do an implicit conversion.  This shouldn't be            // necessary once we support truncation/extension of symbolic values.            if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){ -            rhs = nonloc::ConcreteInt(BasicVals.Convert(resultTy, +            rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType,                                                          rhs_I->getValue()));            } @@ -492,7 +519,8 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,          if (RSym->getType(Context)->isIntegerType()) {            if (const llvm::APSInt *Constant = state->getSymVal(RSym)) {              // The symbol evaluates to a constant. -            const llvm::APSInt &rhs_I = BasicVals.Convert(resultTy, *Constant); +            const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType, +                                                          *Constant);              rhs = nonloc::ConcreteInt(rhs_I);            }          } | 
