diff options
author | Ted Kremenek <kremenek@apple.com> | 2012-03-06 20:06:12 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2012-03-06 20:06:12 +0000 |
commit | 1a45a5ff5d495cb6cd9a3d4d06317af79c0f634d (patch) | |
tree | 2285c98d9e5388e2883db7f902a7ad9edfcdf2ec /lib | |
parent | b3f7542a950ac0d585a7783e825cfe670e05c553 (diff) |
Add static analyzer support for new NSArray/NSDictionary/NSNumber literals.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152139 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp | 120 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/Environment.cpp | 3 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 36 |
3 files changed, 128 insertions, 31 deletions
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 6b14013eea..f7dd6c2127 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1861,41 +1861,49 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, if (!PrevT) { const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); - if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { - // Get the name of the callee (if it is available). - SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); - if (const FunctionDecl *FD = X.getAsFunctionDecl()) - os << "Call to function '" << *FD << '\''; - else - os << "function call"; - } - else { - assert(isa<ObjCMessageExpr>(S)); - // The message expression may have between written directly or as - // a property access. Lazily determine which case we are looking at. - os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method"); - } + if (isa<ObjCArrayLiteral>(S)) { + os << "NSArray literal is an object with a +0 retain count"; + } + else if (isa<ObjCDictionaryLiteral>(S)) { + os << "NSDictionary literal is an object with a +0 retain count"; + } + else { + if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { + // Get the name of the callee (if it is available). + SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); + if (const FunctionDecl *FD = X.getAsFunctionDecl()) + os << "Call to function '" << *FD << '\''; + else + os << "function call"; + } + else { + assert(isa<ObjCMessageExpr>(S)); + // The message expression may have between written directly or as + // a property access. Lazily determine which case we are looking at. + os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method"); + } - if (CurrV.getObjKind() == RetEffect::CF) { - os << " returns a Core Foundation object with a "; - } - else { - assert (CurrV.getObjKind() == RetEffect::ObjC); - os << " returns an Objective-C object with a "; - } + if (CurrV.getObjKind() == RetEffect::CF) { + os << " returns a Core Foundation object with a "; + } + else { + assert (CurrV.getObjKind() == RetEffect::ObjC); + os << " returns an Objective-C object with a "; + } - if (CurrV.isOwned()) { - os << "+1 retain count"; + if (CurrV.isOwned()) { + os << "+1 retain count"; - if (GCEnabled) { - assert(CurrV.getObjKind() == RetEffect::CF); - os << ". " - "Core Foundation objects are not automatically garbage collected."; + if (GCEnabled) { + assert(CurrV.getObjKind() == RetEffect::CF); + os << ". " + "Core Foundation objects are not automatically garbage collected."; + } + } + else { + assert (CurrV.isNotOwned()); + os << "+0 retain count"; } - } - else { - assert (CurrV.isNotOwned()); - os << "+0 retain count"; } PathDiagnosticLocation Pos(S, BRC.getSourceManager(), @@ -2295,6 +2303,8 @@ class RetainCountChecker check::PostStmt<CastExpr>, check::PostStmt<CallExpr>, check::PostStmt<CXXConstructExpr>, + check::PostStmt<ObjCArrayLiteral>, + check::PostStmt<ObjCDictionaryLiteral>, check::PostObjCMessage, check::PreStmt<ReturnStmt>, check::RegionChanges, @@ -2439,7 +2449,10 @@ public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const; + void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const; + void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const; + void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call, CheckerContext &C) const; @@ -2474,6 +2487,8 @@ public: void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, SymbolRef Sym, CheckerContext &C) const; + + void processObjCLiterals(CheckerContext &C, const Expr *Ex) const; const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const; @@ -2637,6 +2652,49 @@ void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE, checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C); } +void RetainCountChecker::processObjCLiterals(CheckerContext &C, + const Expr *Ex) const { + ProgramStateRef state = C.getState(); + const ExplodedNode *pred = C.getPredecessor(); + for (Stmt::const_child_iterator it = Ex->child_begin(), et = Ex->child_end() ; + it != et ; ++it) { + const Stmt *child = *it; + SVal V = state->getSVal(child, pred->getLocationContext()); + if (SymbolRef sym = V.getAsSymbol()) + if (const RefVal* T = state->get<RefBindings>(sym)) { + RefVal::Kind hasErr = (RefVal::Kind) 0; + state = updateSymbol(state, sym, *T, MayEscape, hasErr, C); + if (hasErr) { + processNonLeakError(state, child->getSourceRange(), hasErr, sym, C); + return; + } + } + } + + // Return the object as autoreleased. + // RetEffect RE = RetEffect::MakeNotOwned(RetEffect::ObjC); + if (SymbolRef sym = + state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) { + QualType ResultTy = Ex->getType(); + state = state->set<RefBindings>(sym, RefVal::makeNotOwned(RetEffect::ObjC, + ResultTy)); + } + + C.addTransition(state); +} + +void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL, + CheckerContext &C) const { + // Apply the 'MayEscape' to all values. + processObjCLiterals(C, AL); +} + +void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, + CheckerContext &C) const { + // Apply the 'MayEscape' to all keys and values. + processObjCLiterals(C, DL); +} + void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const { ProgramStateRef state = C.getState(); diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index 5207a506ae..b5ea3db7f3 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -79,6 +79,9 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, else return svalBuilder.makeIntVal(cast<IntegerLiteral>(E)); } + case Stmt::ObjCBoolLiteralExprClass: + return svalBuilder.makeBoolVal(cast<ObjCBoolLiteralExpr>(E)); + // For special C0xx nullptr case, make a null pointer SVal. case Stmt::CXXNullPtrLiteralExprClass: return svalBuilder.makeNull(); diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 7528b4aaa4..408bcd3528 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -555,6 +555,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; + // FIXME. + case Stmt::ObjCSubscriptRefExprClass: + break; + case Stmt::ObjCPropertyRefExprClass: // Implicitly handled by Environment::getSVal(). break; @@ -586,6 +590,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ObjCIsaExprClass: case Stmt::ObjCProtocolExprClass: case Stmt::ObjCSelectorExprClass: + case Expr::ObjCNumericLiteralClass: case Stmt::ParenListExprClass: case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: @@ -602,6 +607,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::IntegerLiteralClass: case Stmt::CharacterLiteralClass: case Stmt::CXXBoolLiteralExprClass: + case Stmt::ObjCBoolLiteralExprClass: case Stmt::FloatingLiteralClass: case Stmt::SizeOfPackExprClass: case Stmt::StringLiteralClass: @@ -616,6 +622,36 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } + case Expr::ObjCArrayLiteralClass: + case Expr::ObjCDictionaryLiteralClass: { + Bldr.takeNodes(Pred); + + ExplodedNodeSet preVisit; + getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); + + // FIXME: explicitly model with a region and the actual contents + // of the container. For now, conjure a symbol. + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr2(preVisit, Tmp, *currentBuilderContext); + + for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end(); + it != et; ++it) { + ExplodedNode *N = *it; + const Expr *Ex = cast<Expr>(S); + QualType resultType = Ex->getType(); + const LocationContext *LCtx = N->getLocationContext(); + SVal result = + svalBuilder.getConjuredSymbolVal(0, Ex, LCtx, resultType, + currentBuilderContext->getCurrentBlockCount()); + ProgramStateRef state = N->getState()->BindExpr(Ex, LCtx, result); + Bldr2.generateNode(S, N, state); + } + + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); + Bldr.addNodes(Dst); + break; + } + case Stmt::ArraySubscriptExprClass: Bldr.takeNodes(Pred); VisitLvalArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst); |