diff options
17 files changed, 153 insertions, 343 deletions
diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h index fb224ef158..07a1ccff6c 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/include/clang/StaticAnalyzer/Core/Checker.h @@ -122,7 +122,7 @@ public: class PreObjCMessage { template <typename CHECKER> - static void _checkObjCMessage(void *checker, const ObjCMessage &msg, + static void _checkObjCMessage(void *checker, const ObjCMethodCall &msg, CheckerContext &C) { ((const CHECKER *)checker)->checkPreObjCMessage(msg, C); } @@ -137,7 +137,7 @@ public: class PostObjCMessage { template <typename CHECKER> - static void _checkObjCMessage(void *checker, const ObjCMessage &msg, + static void _checkObjCMessage(void *checker, const ObjCMethodCall &msg, CheckerContext &C) { ((const CHECKER *)checker)->checkPostObjCMessage(msg, C); } diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 3202fbe6b8..eef82fe848 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -33,7 +33,7 @@ namespace ento { class AnalysisManager; class BugReporter; class CheckerContext; - class ObjCMessage; + class ObjCMethodCall; class SVal; class ExplodedNode; class ExplodedNodeSet; @@ -207,7 +207,7 @@ public: /// \brief Run checkers for pre-visiting obj-c messages. void runCheckersForPreObjCMessage(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, - const ObjCMessage &msg, + const ObjCMethodCall &msg, ExprEngine &Eng) { runCheckersForObjCMessage(/*isPreVisit=*/true, Dst, Src, msg, Eng); } @@ -215,7 +215,7 @@ public: /// \brief Run checkers for post-visiting obj-c messages. void runCheckersForPostObjCMessage(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, - const ObjCMessage &msg, + const ObjCMethodCall &msg, ExprEngine &Eng) { runCheckersForObjCMessage(/*isPreVisit=*/false, Dst, Src, msg, Eng); } @@ -224,7 +224,7 @@ public: void runCheckersForObjCMessage(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, - const ObjCMessage &msg, ExprEngine &Eng); + const ObjCMethodCall &msg, ExprEngine &Eng); /// \brief Run checkers for load/store of a location. void runCheckersForLocation(ExplodedNodeSet &Dst, @@ -343,7 +343,7 @@ public: typedef CheckerFn<void (const Stmt *, CheckerContext &)> CheckStmtFunc; - typedef CheckerFn<void (const ObjCMessage &, CheckerContext &)> + typedef CheckerFn<void (const ObjCMethodCall &, CheckerContext &)> CheckObjCMessageFunc; typedef CheckerFn<void (const SVal &location, bool isLoad, diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h index fdfb485a52..a16eb4afef 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h @@ -20,8 +20,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" namespace clang { @@ -114,6 +113,10 @@ public: /// \brief Returns the kind of call this is. Kind getKind() const { return K; } + /// \brief Returns a source range for the entire call, suitable for + /// outputting in diagnostics. + virtual SourceRange getSourceRange() const = 0; + /// \brief Returns the value of a given argument at the time of the call. virtual SVal getArgSVal(unsigned Index) const; @@ -213,6 +216,7 @@ public: const FunctionDecl *getDecl() const; unsigned getNumArgs() const { return CE->getNumArgs(); } + SourceRange getSourceRange() const { return CE->getSourceRange(); } const Expr *getArgExpr(unsigned Index) const { return CE->getArg(Index); @@ -305,6 +309,7 @@ public: : AnyFunctionCall(St, LCtx, CE_CXXConstructor), CE(ce), Target(target) {} const CXXConstructExpr *getOriginExpr() const { return CE; } + SourceRange getSourceRange() const { return CE->getSourceRange(); } const CXXConstructorDecl *getDecl() const { return CE->getConstructor(); @@ -341,7 +346,9 @@ public: Selector getSelector() const { return Msg->getSelector(); } bool isInstanceMessage() const { return Msg->isInstanceMessage(); } ObjCMethodFamily getMethodFamily() const { return Msg->getMethodFamily(); } + const ObjCMethodDecl *getDecl() const { return Msg->getMethodDecl(); } + SourceRange getSourceRange() const { return Msg->getSourceRange(); } unsigned getNumArgs() const { return Msg->getNumArgs(); } const Expr *getArgExpr(unsigned Index) const { return Msg->getArg(Index); @@ -351,6 +358,10 @@ public: SVal getReceiverSVal() const; + const Expr *getInstanceReceiverExpr() const { + return Msg->getInstanceReceiver(); + } + const ObjCInterfaceDecl *getReceiverInterface() const { return Msg->getReceiverInterface(); } @@ -359,11 +370,6 @@ public: return Msg->getReceiverRange(); } - // FIXME: Remove this once everything is converted to use ObjCMethodCall. - virtual operator ObjCMessage() const { - return ObjCMessage(Msg); - } - static bool classof(const CallEvent *CA) { return CA->getKind() >= CE_BEG_OBJC_CALLS && CA->getKind() <= CE_END_OBJC_CALLS; @@ -389,20 +395,23 @@ public: /// Example: obj.prop += 1; class ObjCPropertyAccess : public ObjCMethodCall { const ObjCPropertyRefExpr *PropE; + SourceRange EntireRange; public: - ObjCPropertyAccess(const ObjCPropertyRefExpr *pe, const ObjCMessageExpr *Msg, - const ProgramStateRef St, const LocationContext *LCtx) - : ObjCMethodCall(Msg, St, LCtx, CE_ObjCPropertyAccess), PropE(pe) {} + ObjCPropertyAccess(const ObjCPropertyRefExpr *pe, SourceRange range, + const ObjCMessageExpr *Msg, const ProgramStateRef St, + const LocationContext *LCtx) + : ObjCMethodCall(Msg, St, LCtx, CE_ObjCPropertyAccess), PropE(pe), + EntireRange(range) + {} /// \brief Returns true if this property access is calling the setter method. bool isSetter() const { return getNumArgs() > 0; } - // FIXME: Remove this once everything is converted to use ObjCMethodCall. - operator ObjCMessage() const { - return ObjCMessage(getOriginExpr(), PropE, isSetter()); + SourceRange getSourceRange() const { + return EntireRange; } static bool classof(const CallEvent *CA) { diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h deleted file mode 100644 index f64326d2c6..0000000000 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h +++ /dev/null @@ -1,163 +0,0 @@ -//===- ObjCMessage.h - Wrapper for ObjC messages and dot syntax ---*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ObjCMessage which serves as a common wrapper for ObjC -// message expressions or implicit messages for loading/storing ObjC properties. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_OBJCMESSAGE -#define LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_OBJCMESSAGE - -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/ExprCXX.h" -#include "clang/Basic/SourceManager.h" -#include "llvm/ADT/PointerUnion.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Support/Compiler.h" - -namespace clang { -namespace ento { -using llvm::StrInStrNoCase; - -/// \brief Represents both explicit ObjC message expressions and implicit -/// messages that are sent for handling properties in dot syntax. -class ObjCMessage { - const ObjCMessageExpr *Msg; - const ObjCPropertyRefExpr *PE; - const bool IsPropSetter; -public: - ObjCMessage() : Msg(0), PE(0), IsPropSetter(false) {} - - ObjCMessage(const ObjCMessageExpr *E, const ObjCPropertyRefExpr *pe = 0, - bool isSetter = false) - : Msg(E), PE(pe), IsPropSetter(isSetter) { - assert(E && "should not be initialized with null expression"); - } - - bool isValid() const { return Msg; } - - bool isPureMessageExpr() const { return !PE; } - - bool isPropertyGetter() const { return PE && !IsPropSetter; } - - bool isPropertySetter() const { - return IsPropSetter; - } - - const ObjCMessageExpr *getMessageExpr() const { - return Msg; - } - - QualType getType(ASTContext &ctx) const { - return Msg->getType(); - } - - QualType getResultType(ASTContext &ctx) const { - if (const ObjCMethodDecl *MD = Msg->getMethodDecl()) - return MD->getResultType(); - return getType(ctx); - } - - ObjCMethodFamily getMethodFamily() const { - return Msg->getMethodFamily(); - } - - Selector getSelector() const { - return Msg->getSelector(); - } - - const Expr *getInstanceReceiver() const { - return Msg->getInstanceReceiver(); - } - - SVal getInstanceReceiverSVal(ProgramStateRef State, - const LocationContext *LC) const { - if (!isInstanceMessage()) - return UndefinedVal(); - if (const Expr *Ex = getInstanceReceiver()) - return State->getSValAsScalarOrLoc(Ex, LC); - - // An instance message with no expression means we are sending to super. - // In this case the object reference is the same as 'self'. - const ImplicitParamDecl *SelfDecl = LC->getSelfDecl(); - assert(SelfDecl && "No message receiver Expr, but not in an ObjC method"); - return State->getSVal(State->getRegion(SelfDecl, LC)); - } - - bool isInstanceMessage() const { - return Msg->isInstanceMessage(); - } - - const ObjCMethodDecl *getMethodDecl() const { - return Msg->getMethodDecl(); - } - - const ObjCInterfaceDecl *getReceiverInterface() const { - return Msg->getReceiverInterface(); - } - - SourceLocation getSuperLoc() const { - if (PE) - return PE->getReceiverLocation(); - return Msg->getSuperLoc(); - } - - SourceRange getSourceRange() const LLVM_READONLY { - if (PE) - return PE->getSourceRange(); - return Msg->getSourceRange(); - } - - unsigned getNumArgs() const { - return Msg->getNumArgs(); - } - - SVal getArgSVal(unsigned i, - const LocationContext *LCtx, - ProgramStateRef state) const { - assert(i < getNumArgs() && "Invalid index for argument"); - return state->getSVal(Msg->getArg(i), LCtx); - } - - QualType getArgType(unsigned i) const { - assert(i < getNumArgs() && "Invalid index for argument"); - return Msg->getArg(i)->getType(); - } - - const Expr *getArgExpr(unsigned i) const { - assert(i < getNumArgs() && "Invalid index for argument"); - return Msg->getArg(i); - } - - SourceRange getArgSourceRange(unsigned i) const { - const Expr *argE = getArgExpr(i); - return argE->getSourceRange(); - } - - SourceRange getReceiverSourceRange() const { - if (PE) { - if (PE->isObjectReceiver()) - return PE->getBase()->getSourceRange(); - } - else { - return Msg->getReceiverRange(); - } - - // FIXME: This isn't a range. - return PE->getReceiverLocation(); - } -}; - -} -} - -#endif diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index c75c86faf4..1d0c4b336b 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -17,10 +17,10 @@ #include "clang/Analysis/DomainSpecific/CocoaConventions.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" @@ -46,10 +46,10 @@ public: // Utility functions. //===----------------------------------------------------------------------===// -static const char* GetReceiverNameType(const ObjCMessage &msg) { +static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) { if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) - return ID->getIdentifier()->getNameStart(); - return 0; + return ID->getIdentifier()->getName(); + return StringRef(); } enum FoundationClass { @@ -95,15 +95,15 @@ namespace { mutable OwningPtr<APIMisuse> BT; void WarnNilArg(CheckerContext &C, - const ObjCMessage &msg, unsigned Arg) const; + const ObjCMethodCall &msg, unsigned Arg) const; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; }; } void NilArgChecker::WarnNilArg(CheckerContext &C, - const ObjCMessage &msg, + const ObjCMethodCall &msg, unsigned int Arg) const { if (!BT) @@ -112,7 +112,7 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, if (ExplodedNode *N = C.generateSink()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); - os << "Argument to '" << GetReceiverNameType(msg) << "' method '" + os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" << msg.getSelector().getAsString() << "' cannot be nil"; BugReport *R = new BugReport(*BT, os.str(), N); @@ -121,7 +121,7 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, } } -void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, +void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); if (!ID) @@ -151,7 +151,7 @@ void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, Name == "compare:options:range:locale:" || Name == "componentsSeparatedByCharactersInSet:" || Name == "initWithFormat:") { - if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState()))) + if (isNil(msg.getArgSVal(0))) WarnNilArg(C, msg, 0); } } @@ -455,11 +455,11 @@ class ClassReleaseChecker : public Checker<check::PreObjCMessage> { mutable OwningPtr<BugType> BT; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; }; } -void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg, +void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { @@ -511,18 +511,18 @@ class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { mutable Selector initWithObjectsAndKeysS; mutable OwningPtr<BugType> BT; - bool isVariadicMessage(const ObjCMessage &msg) const; + bool isVariadicMessage(const ObjCMethodCall &msg) const; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; }; } /// isVariadicMessage - Returns whether the given message is a variadic message, /// where all arguments must be Objective-C types. bool -VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { - const ObjCMethodDecl *MD = msg.getMethodDecl(); +VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { + const ObjCMethodDecl *MD = msg.getDecl(); if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) return false; @@ -566,7 +566,7 @@ VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { } } -void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, +void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " @@ -602,7 +602,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, ProgramStateRef state = C.getState(); for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { - QualType ArgTy = msg.getArgType(I); + QualType ArgTy = msg.getArgExpr(I)->getType(); if (ArgTy->isObjCObjectPointerType()) continue; @@ -611,8 +611,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, continue; // Ignore pointer constants. - if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(), - state))) + if (isa<loc::ConcreteInt>(msg.getArgSVal(I))) continue; // Ignore pointer types annotated with 'NSObject' attribute. @@ -624,9 +623,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, continue; // Generate only one error node to use for all bug reports. - if (!errorNode.hasValue()) { + if (!errorNode.hasValue()) errorNode = C.addTransition(); - } if (!errorNode.getValue()) continue; @@ -634,17 +632,18 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); - if (const char *TypeName = GetReceiverNameType(msg)) + StringRef TypeName = GetReceiverInterfaceName(msg); + if (!TypeName.empty()) os << "Argument to '" << TypeName << "' method '"; else os << "Argument to method '"; os << msg.getSelector().getAsString() - << "' should be an Objective-C pointer type, not '" - << ArgTy.getAsString() << "'"; + << "' should be an Objective-C pointer type, not '"; + ArgTy.print(os, C.getLangOpts()); + os << "'"; - BugReport *R = new BugReport(*BT, os.str(), - errorNode.getValue()); + BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 30be60c9a6..083f21ea3f 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -17,7 +17,6 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/TargetInfo.h" @@ -39,7 +38,7 @@ class CallAndMessageChecker public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; private: static void PreVisitProcessArgs(CheckerContext &C, const CallEvent &Call, @@ -51,12 +50,12 @@ private: OwningPtr<BugType> &BT); static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); - void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg, + void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, ExplodedNode *N) const; void HandleNilReceiver(CheckerContext &C, ProgramStateRef state, - ObjCMessage msg) const; + const ObjCMethodCall &msg) const; static void LazyInit_BT(const char *desc, OwningPtr<BugType> &BT) { if (!BT) @@ -248,65 +247,61 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, } } -void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, +void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { + SVal recVal = msg.getReceiverSVal(); + if (recVal.isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + BugType *BT = 0; + if (isa<ObjCPropertyAccess>(msg)) { + if (!BT_objc_prop_undef) + BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " + "uninitialized object pointer")); + BT = BT_objc_prop_undef.get(); + } else { + if (!BT_msg_undef) + BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " + "is an uninitialized value")); + BT = BT_msg_undef.get(); + } + BugReport *R = new BugReport(*BT, BT->getName(), N); + R->addRange(msg.getReceiverSourceRange()); - ProgramStateRef state = C.getState(); - const LocationContext *LCtx = C.getLocationContext(); - - // FIXME: Handle 'super'? - if (const Expr *receiver = msg.getInstanceReceiver()) { - SVal recVal = state->getSVal(receiver, LCtx); - if (recVal.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { - BugType *BT = 0; - if (msg.isPureMessageExpr()) { - if (!BT_msg_undef) - BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " - "is an uninitialized value")); - BT = BT_msg_undef.get(); - } - else { - if (!BT_objc_prop_undef) - BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " - "uninitialized object pointer")); - BT = BT_objc_prop_undef.get(); - } - BugReport *R = - new BugReport(*BT, BT->getName(), N); - R->addRange(receiver->getSourceRange()); + // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet. + if (const Expr *ReceiverE = msg.getInstanceReceiverExpr()) R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - receiver, + ReceiverE, R)); - C.EmitReport(R); - } + C.EmitReport(R); + } + return; + } else { + // Bifurcate the state into nil and non-nil ones. + DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); + + ProgramStateRef state = C.getState(); + ProgramStateRef notNilState, nilState; + llvm::tie(notNilState, nilState) = state->assume(receiverVal); + + // Handle receiver must be nil. + if (nilState && !notNilState) { + HandleNilReceiver(C, state, msg); return; - } else { - // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); - - ProgramStateRef notNilState, nilState; - llvm::tie(notNilState, nilState) = state->assume(receiverVal); - - // Handle receiver must be nil. - if (nilState && !notNilState) { - HandleNilReceiver(C, state, msg); - return; - } } } - const char *bugDesc = msg.isPropertySetter() ? - "Argument for property setter is an uninitialized value" - : "Argument in message expression is an uninitialized value"; + const char *bugDesc = "Argument in message expression is an " + "uninitialized value"; + if (const ObjCPropertyAccess *Prop = dyn_cast<ObjCPropertyAccess>(&msg)) + if (Prop->isSetter()) + bugDesc = "Argument for property setter is an uninitialized value"; + // Check for any arguments that are uninitialized/undefined. - // FIXME: ObjCMessage is set to be removed soon. - PreVisitProcessArgs(C, ObjCMessageSend(msg.getMessageExpr(), state, LCtx), - bugDesc, BT_msg_arg); + PreVisitProcessArgs(C, msg, bugDesc, BT_msg_arg); } void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, - const ObjCMessage &msg, + const ObjCMethodCall &msg, ExplodedNode *N) const { if (!BT_msg_ret) @@ -317,11 +312,13 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The receiver of message '" << msg.getSelector().getAsString() - << "' is nil and returns a value of type '" - << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage"; + << "' is nil and returns a value of type '"; + msg.getResultType().print(os, C.getLangOpts()); + os << "' that will be garbage"; BugReport *report = new BugReport(*BT_msg_ret, os.str(), N); - if (const Expr *receiver = msg.getInstanceReceiver()) { + // FIXME: This won't track "self" in messages to super. + if (const Expr *receiver = msg.getInstanceReceiverExpr()) { report->addRange(receiver->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, receiver, @@ -338,25 +335,25 @@ static bool supportsNilWithFloatRet(const llvm::Triple &triple) { void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, ProgramStateRef state, - ObjCMessage msg) const { + const ObjCMethodCall &Msg) const { ASTContext &Ctx = C.getASTContext(); // Check the return type of the message expression. A message to nil will // return different values depending on the return type and the architecture. - QualType RetTy = msg.getType(Ctx); + QualType RetTy = Msg.getResultType(); CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); const LocationContext *LCtx = C.getLocationContext(); if (CanRetTy->isStructureOrClassType()) { // Structure returns are safe since the compiler zeroes them out. - SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); - C.addTransition(state->BindExpr(msg.getMessageExpr(), LCtx, V)); + SVal V = C.getSValBuilder().makeZeroVal(RetTy); + C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V)); return; } // Other cases: check if sizeof(return type) > sizeof(void*) if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap() - .isConsumedExpr(msg.getMessageExpr())) { + .isConsumedExpr(Msg.getOriginExpr())) { // Compute: sizeof(void *) and sizeof(return type) const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); @@ -369,7 +366,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy))) { if (ExplodedNode *N = C.generateSink(state)) - emitNilReceiverBug(C, msg, N); + emitNilReceiverBug(C, Msg, N); return; } @@ -386,8 +383,8 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, // it most likely isn't nil. We should assume the semantics // of this case unless we have *a lot* more knowledge. // - SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); - C.addTransition(state->BindExpr(msg.getMessageExpr(), LCtx, V)); + SVal V = C.getSValBuilder().makeZeroVal(RetTy); + C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V)); return; } diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp index 61582d028a..751b8f4c89 100644 --- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -76,10 +76,10 @@ public: void checkPostStmt(const CallExpr *DS, CheckerContext &C) const; /// \brief Pre-visit the Objective C messages. - void checkPreObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const {} + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {} /// \brief Post-visit the Objective C messages. - void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const {} + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {} /// \brief Pre-visit of the condition statement of a branch (such as IfStmt). void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {} diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 2c960921a4..41cd80e01e 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -124,7 +124,7 @@ public: void checkPreStmt(const CallExpr *S, CheckerContext &C) const; void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; - void checkPreObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const; void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; void checkEndPath(CheckerContext &C) const; @@ -491,29 +491,21 @@ static bool isFreeWhenDoneSetToZero(const ObjCMethodCall &Call) { return false; } -void MallocChecker::checkPreObjCMessage(const ObjCMessage &Msg, +void MallocChecker::checkPreObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const { - const ObjCMethodDecl *MD = Msg.getMethodDecl(); - if (!MD) - return; - - // FIXME: ObjCMessage is going away soon. - ObjCMessageSend Call(Msg.getMessageExpr(), C.getState(), - C.getLocationContext()); - Selector S = Msg.getSelector(); - // If the first selector is dataWithBytesNoCopy, assume that the memory will // be released with 'free' by the new object. // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; // Unless 'freeWhenDone' param set to 0. // TODO: Check that the memory was allocated with malloc. + Selector S = Call.getSelector(); if ((S.getNameForSlot(0) == "dataWithBytesNoCopy" || S.getNameForSlot(0) == "initWithBytesNoCopy" || S.getNameForSlot(0) == "initWithCharactersNoCopy") && !isFreeWhenDoneSetToZero(Call)){ unsigned int argIdx = 0; C.addTransition(FreeMemAux(C, Call.getArgExpr(argIdx), - Msg.getMessageExpr(), C.getState(), true)); + Call.getOriginExpr(), C.getState(), true)); } } diff --git a/lib/StaticAnalyzer/Checkers/NSAu |