Author: dcoughlin Date: Fri Feb 5 13:17:16 2016 New Revision: 259905 URL: http://llvm.org/viewvc/llvm-project?rev=259905&view=rev Log: [analyzer] checker-277: Pull in nullability fixes and dealloc checker.
Cherry-pick in the following fixes from trunk: r259288:[analyzer] Make suppression of macro defensive checks work with -analyzer-eagerly-assume. r259222:[analyzer] Suppress null reports from defensive checks in function-like macros. r259221:[analyzer] Improve Nullability checker diagnostics r259118:[analyzer] NullabilityChecker: Remove unused isReturnSelf() function. r259099:[analyzer] Suppress nullability warnings in copy, mutableCopy, and init families. r258461:[analyzer] Suppress nullability warning for defensive super initializer idiom. r258896:[analyzer] ObjCDeallocChecker: Only operate on classes with retained properties. r258886:[analyzer] Body farm: Look for property ivar in shadowing readwrite property. r258061:[analyzer] Nullability: Look through implicit casts when suppressing warnings on return. r257938:[analyzer] Check for return of nil in ObjC methods with nonnull return type. Added: cfe/tags/checker/checker-278/test/Analysis/DeallocMissingRelease.m - copied unchanged from r258896, cfe/trunk/test/Analysis/DeallocMissingRelease.m cfe/tags/checker/checker-278/test/Analysis/nullability-no-arc.mm - copied unchanged from r258061, cfe/trunk/test/Analysis/nullability-no-arc.mm Modified: cfe/tags/checker/checker-278/ (props changed) cfe/tags/checker/checker-278/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h cfe/tags/checker/checker-278/lib/Analysis/BodyFarm.cpp cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp cfe/tags/checker/checker-278/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp cfe/tags/checker/checker-278/lib/StaticAnalyzer/Core/CheckerContext.cpp cfe/tags/checker/checker-278/test/Analysis/MissingDealloc.m cfe/tags/checker/checker-278/test/Analysis/PR2978.m cfe/tags/checker/checker-278/test/Analysis/inlining/false-positive-suppression.c cfe/tags/checker/checker-278/test/Analysis/nullability.mm cfe/tags/checker/checker-278/test/Analysis/nullability_nullonly.mm cfe/tags/checker/checker-278/test/Analysis/properties.m Propchange: cfe/tags/checker/checker-278/ ------------------------------------------------------------------------------ --- svn:mergeinfo (original) +++ svn:mergeinfo Fri Feb 5 13:17:16 2016 @@ -1,3 +1,4 @@ /cfe/branches/type-system-rewrite:134693-134817 +/cfe/trunk:257938,258061,258461,258886,258896,259099,259118,259221-259222,259288 /cfe/trunk/test:170344 /cfe/trunk/test/SemaTemplate:126920 Modified: cfe/tags/checker/checker-278/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h (original) +++ cfe/tags/checker/checker-278/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h Fri Feb 5 13:17:16 2016 @@ -263,6 +263,10 @@ public: Eng.getBugReporter().emitReport(std::move(R)); } + /// \brief Returns the word that should be used to refer to the declaration + /// in the report. + StringRef getDeclDescription(const Decl *D); + /// \brief Get the declaration of the called function (path-sensitive). const FunctionDecl *getCalleeDecl(const CallExpr *CE) const; Modified: cfe/tags/checker/checker-278/lib/Analysis/BodyFarm.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/lib/Analysis/BodyFarm.cpp?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/lib/Analysis/BodyFarm.cpp (original) +++ cfe/tags/checker/checker-278/lib/Analysis/BodyFarm.cpp Fri Feb 5 13:17:16 2016 @@ -383,10 +383,49 @@ Stmt *BodyFarm::getBody(const FunctionDe return Val.getValue(); } +static const ObjCIvarDecl *findBackingIvar(const ObjCPropertyDecl *Prop) { + const ObjCIvarDecl *IVar = Prop->getPropertyIvarDecl(); + + if (IVar) + return IVar; + + // When a readonly property is shadowed in a class extensions with a + // a readwrite property, the instance variable belongs to the shadowing + // property rather than the shadowed property. If there is no instance + // variable on a readonly property, check to see whether the property is + // shadowed and if so try to get the instance variable from shadowing + // property. + if (!Prop->isReadOnly()) + return nullptr; + + auto *Container = cast<ObjCContainerDecl>(Prop->getDeclContext()); + const ObjCInterfaceDecl *PrimaryInterface = nullptr; + if (auto *InterfaceDecl = dyn_cast<ObjCInterfaceDecl>(Container)) { + PrimaryInterface = InterfaceDecl; + } else if (auto *CategoryDecl = dyn_cast<ObjCCategoryDecl>(Container)) { + PrimaryInterface = CategoryDecl->getClassInterface(); + } else if (auto *ImplDecl = dyn_cast<ObjCImplDecl>(Container)) { + PrimaryInterface = ImplDecl->getClassInterface(); + } else { + return nullptr; + } + + // FindPropertyVisibleInPrimaryClass() looks first in class extensions, so it + // is guaranteed to find the shadowing property, if it exists, rather than + // the shadowed property. + auto *ShadowingProp = PrimaryInterface->FindPropertyVisibleInPrimaryClass( + Prop->getIdentifier()); + if (ShadowingProp && ShadowingProp != Prop) { + IVar = ShadowingProp->getPropertyIvarDecl(); + } + + return IVar; +} + static Stmt *createObjCPropertyGetter(ASTContext &Ctx, const ObjCPropertyDecl *Prop) { // First, find the backing ivar. - const ObjCIvarDecl *IVar = Prop->getPropertyIvarDecl(); + const ObjCIvarDecl *IVar = findBackingIvar(Prop); if (!IVar) return nullptr; Modified: cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp (original) +++ cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp Fri Feb 5 13:17:16 2016 @@ -28,7 +28,7 @@ using namespace clang; using namespace ento; -static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID, +static bool scan_ivar_release(Stmt *S, const ObjCIvarDecl *ID, const ObjCPropertyDecl *PD, Selector Release, IdentifierInfo* SelfII, @@ -76,42 +76,67 @@ static bool scan_ivar_release(Stmt *S, O return false; } +static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, + const ObjCIvarDecl **ID, + const ObjCPropertyDecl **PD) { + + if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + return false; + + (*ID) = I->getPropertyIvarDecl(); + if (!(*ID)) + return false; + + QualType T = (*ID)->getType(); + if (!T->isObjCRetainableType()) + return false; + + (*PD) = I->getPropertyDecl(); + // Shouldn't be able to synthesize a property that doesn't exist. + assert(*PD); + + return true; +} + +static bool synthesizedPropertyRequiresRelease(const ObjCPropertyDecl *PD) { + // A synthesized property must be released if and only if the kind of setter + // was neither 'assign' or 'weak'. + ObjCPropertyDecl::SetterKind SK = PD->getSetterKind(); + return (SK != ObjCPropertyDecl::Assign && SK != ObjCPropertyDecl::Weak); +} + static void checkObjCDealloc(const CheckerBase *Checker, const ObjCImplementationDecl *D, const LangOptions &LOpts, BugReporter &BR) { - assert (LOpts.getGC() != LangOptions::GCOnly); + assert(LOpts.getGC() != LangOptions::GCOnly); + assert(!LOpts.ObjCAutoRefCount); ASTContext &Ctx = BR.getContext(); const ObjCInterfaceDecl *ID = D->getClassInterface(); - // Does the class contain any ivars that are pointers (or id<...>)? + // Does the class contain any synthesized properties that are retainable? // If not, skip the check entirely. - // NOTE: This is motivated by PR 2517: - // http://llvm.org/bugs/show_bug.cgi?id=2517 - - bool containsPointerIvar = false; - - for (const auto *Ivar : ID->ivars()) { - QualType T = Ivar->getType(); - - if (!T->isObjCObjectPointerType() || - Ivar->hasAttr<IBOutletAttr>() || // Skip IBOutlets. - Ivar->hasAttr<IBOutletCollectionAttr>()) // Skip IBOutletCollections. + bool containsRetainedSynthesizedProperty = false; + for (const auto *I : D->property_impls()) { + const ObjCIvarDecl *ID = nullptr; + const ObjCPropertyDecl *PD = nullptr; + if (!isSynthesizedRetainableProperty(I, &ID, &PD)) continue; - containsPointerIvar = true; - break; + if (synthesizedPropertyRequiresRelease(PD)) { + containsRetainedSynthesizedProperty = true; + break; + } } - if (!containsPointerIvar) + if (!containsRetainedSynthesizedProperty) return; // Determine if the class subclasses NSObject. IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); - for ( ; ID ; ID = ID->getSuperClass()) { IdentifierInfo *II = ID->getIdentifier(); @@ -142,9 +167,6 @@ static void checkObjCDealloc(const Check } } - PathDiagnosticLocation DLoc = - PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); - if (!MD) { // No dealloc found. const char* name = LOpts.getGC() == LangOptions::NonGC @@ -155,6 +177,9 @@ static void checkObjCDealloc(const Check llvm::raw_string_ostream os(buf); os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; + PathDiagnosticLocation DLoc = + PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); + BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC, os.str(), DLoc); return; @@ -170,28 +195,12 @@ static void checkObjCDealloc(const Check // Scan for missing and extra releases of ivars used by implementations // of synthesized properties for (const auto *I : D->property_impls()) { - // We can only check the synthesized properties - if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) - continue; - - ObjCIvarDecl *ID = I->getPropertyIvarDecl(); - if (!ID) - continue; - - QualType T = ID->getType(); - if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars - continue; - - const ObjCPropertyDecl *PD = I->getPropertyDecl(); - if (!PD) - continue; - - // ivars cannot be set via read-only properties, so we'll skip them - if (PD->isReadOnly()) + const ObjCIvarDecl *ID = nullptr; + const ObjCPropertyDecl *PD = nullptr; + if (!isSynthesizedRetainableProperty(I, &ID, &PD)) continue; - // ivar must be released if and only if the kind of setter was not 'assign' - bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; + bool requiresRelease = synthesizedPropertyRequiresRelease(PD); if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) != requiresRelease) { const char *name = nullptr; @@ -203,24 +212,28 @@ static void checkObjCDealloc(const Check ? "missing ivar release (leak)" : "missing ivar release (Hybrid MM, non-GC)"; - os << "The '" << *ID - << "' instance variable was retained by a synthesized property but " - "wasn't released in 'dealloc'"; + os << "The '" << *ID << "' instance variable in '" << *D + << "' was retained by a synthesized property " + "but was not released in 'dealloc'"; } else { name = LOpts.getGC() == LangOptions::NonGC ? "extra ivar release (use-after-release)" : "extra ivar release (Hybrid MM, non-GC)"; - os << "The '" << *ID - << "' instance variable was not retained by a synthesized property " + os << "The '" << *ID << "' instance variable in '" << *D + << "' was not retained by a synthesized property " "but was released in 'dealloc'"; } - PathDiagnosticLocation SDLoc = - PathDiagnosticLocation::createBegin(I, BR.getSourceManager()); + // If @synthesize statement is missing, fall back to @property statement. + const Decl *SPDecl = I->getLocation().isValid() + ? static_cast<const Decl *>(I) + : static_cast<const Decl *>(PD); + PathDiagnosticLocation SPLoc = + PathDiagnosticLocation::createBegin(SPDecl, BR.getSourceManager()); BR.EmitBasicReport(MD, Checker, name, - categories::CoreFoundationObjectiveC, os.str(), SDLoc); + categories::CoreFoundationObjectiveC, os.str(), SPLoc); } } } @@ -235,7 +248,8 @@ class ObjCDeallocChecker : public Checke public: void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, BugReporter &BR) const { - if (mgr.getLangOpts().getGC() == LangOptions::GCOnly) + if (mgr.getLangOpts().getGC() == LangOptions::GCOnly || + mgr.getLangOpts().ObjCAutoRefCount) return; checkObjCDealloc(this, cast<ObjCImplementationDecl>(D), mgr.getLangOpts(), BR); Modified: cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp (original) +++ cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp Fri Feb 5 13:17:16 2016 @@ -230,7 +230,7 @@ void DereferenceChecker::checkLocation(S // dereference. if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) { ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(), - /*IsDirectDereference=*/false}; + /*IsDirectDereference=*/true}; dispatchEvent(event); } } @@ -272,7 +272,7 @@ void DereferenceChecker::checkBind(SVal if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N, &C.getBugReporter(), - /*IsDirectDereference=*/false}; + /*IsDirectDereference=*/true}; dispatchEvent(event); } } Modified: cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp (original) +++ cfe/tags/checker/checker-278/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp Fri Feb 5 13:17:16 2016 @@ -26,13 +26,16 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "llvm/Support/Path.h" + #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Path.h" + using namespace clang; using namespace ento; @@ -89,18 +92,6 @@ enum class ErrorKind : int { NullablePassedToNonnull }; -const char *const ErrorMessages[] = { - "Null is assigned to a pointer which is expected to have non-null value", - "Null passed to a callee that requires a non-null argument", - "Null is returned from a function that is expected to return a non-null " - "value", - "Nullable pointer is assigned to a pointer which is expected to have " - "non-null value", - "Nullable pointer is returned from a function that is expected to return a " - "non-null value", - "Nullable pointer is dereferenced", - "Nullable pointer is passed to a callee that requires a non-null argument"}; - class NullabilityChecker : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>, check::PostCall, check::PostStmt<ExplicitCastExpr>, @@ -169,17 +160,19 @@ private: /// /// When \p SuppressPath is set to true, no more bugs will be reported on this /// path by this checker. - void reportBugIfPreconditionHolds(ErrorKind Error, ExplodedNode *N, - const MemRegion *Region, CheckerContext &C, + void reportBugIfPreconditionHolds(StringRef Msg, ErrorKind Error, + ExplodedNode *N, const MemRegion *Region, + CheckerContext &C, const Stmt *ValueExpr = nullptr, bool SuppressPath = false) const; - void reportBug(ErrorKind Error, ExplodedNode *N, const MemRegion *Region, - BugReporter &BR, const Stmt *ValueExpr = nullptr) const { + void reportBug(StringRef Msg, ErrorKind Error, ExplodedNode *N, + const MemRegion *Region, BugReporter &BR, + const Stmt *ValueExpr = nullptr) const { if (!BT) BT.reset(new BugType(this, "Nullability", "Memory error")); - const char *Msg = ErrorMessages[static_cast<int>(Error)]; - std::unique_ptr<BugReport> R(new BugReport(*BT, Msg, N)); + + auto R = llvm::make_unique<BugReport>(*BT, Msg, N); if (Region) { R->markInteresting(Region); R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region)); @@ -366,29 +359,25 @@ static bool checkPreconditionViolation(P if (!D) return false; - if (const auto *BlockD = dyn_cast<BlockDecl>(D)) { - if (checkParamsForPreconditionViolation(BlockD->parameters(), State, - LocCtxt)) { - if (!N->isSink()) - C.addTransition(State->set<PreconditionViolated>(true), N); - return true; - } + ArrayRef<ParmVarDecl*> Params; + if (const auto *BD = dyn_cast<BlockDecl>(D)) + Params = BD->parameters(); + else if (const auto *FD = dyn_cast<FunctionDecl>(D)) + Params = FD->parameters(); + else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) + Params = MD->parameters(); + else return false; - } - if (const auto *FuncDecl = dyn_cast<FunctionDecl>(D)) { - if (checkParamsForPreconditionViolation(FuncDecl->parameters(), State, - LocCtxt)) { - if (!N->isSink()) - C.addTransition(State->set<PreconditionViolated>(true), N); - return true; - } - return false; + if (checkParamsForPreconditionViolation(Params, State, LocCtxt)) { + if (!N->isSink()) + C.addTransition(State->set<PreconditionViolated>(true), N); + return true; } return false; } -void NullabilityChecker::reportBugIfPreconditionHolds( +void NullabilityChecker::reportBugIfPreconditionHolds(StringRef Msg, ErrorKind Error, ExplodedNode *N, const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const { ProgramStateRef OriginalState = N->getState(); @@ -400,7 +389,7 @@ void NullabilityChecker::reportBugIfPrec N = C.addTransition(OriginalState, N); } - reportBug(Error, N, Region, C.getBugReporter(), ValueExpr); + reportBug(Msg, Error, N, Region, C.getBugReporter(), ValueExpr); } /// Cleaning up the program state. @@ -454,12 +443,30 @@ void NullabilityChecker::checkEvent(Impl // Do not suppress errors on defensive code paths, because dereferencing // a nullable pointer is always an error. if (Event.IsDirectDereference) - reportBug(ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR); - else - reportBug(ErrorKind::NullablePassedToNonnull, Event.SinkNode, Region, BR); + reportBug("Nullable pointer is dereferenced", + ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR); + else { + reportBug("Nullable pointer is passed to a callee that requires a " + "non-null", ErrorKind::NullablePassedToNonnull, + Event.SinkNode, Region, BR); + } } } +/// Find the outermost subexpression of E that is not an implicit cast. +/// This looks through the implicit casts to _Nonnull that ARC adds to +/// return expressions of ObjC types when the return type of the function or +/// method is non-null but the express is not. +static const Expr *lookThroughImplicitCasts(const Expr *E) { + assert(E); + + while (auto *ICE = dyn_cast<ImplicitCastExpr>(E)) { + E = ICE->getSubExpr(); + } + + return E; +} + /// This method check when nullable pointer or null value is returned from a /// function that has nonnull return type. /// @@ -484,16 +491,31 @@ void NullabilityChecker::checkPreStmt(co if (!RetSVal) return; + bool InSuppressedMethodFamily = false; + + QualType RequiredRetType; AnalysisDeclContext *DeclCtxt = C.getLocationContext()->getAnalysisDeclContext(); - const FunctionType *FuncType = DeclCtxt->getDecl()->getFunctionType(); - if (!FuncType) + const Decl *D = DeclCtxt->getDecl(); + if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) { + // HACK: This is a big hammer to avoid warning when there are defensive + // nil checks in -init and -copy methods. We should add more sophisticated + // logic here to suppress on common defensive idioms but still + // warn when there is a likely problem. + ObjCMethodFamily Family = MD->getMethodFamily(); + if (OMF_init == Family || OMF_copy == Family || OMF_mutableCopy == Family) + InSuppressedMethodFamily = true; + + RequiredRetType = MD->getReturnType(); + } else if (auto *FD = dyn_cast<FunctionDecl>(D)) { + RequiredRetType = FD->getReturnType(); + } else { return; + } NullConstraint Nullness = getNullConstraint(*RetSVal, State); - Nullability RequiredNullability = - getNullabilityAnnotation(FuncType->getReturnType()); + Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType); // If the returned value is null but the type of the expression // generating it is nonnull then we will suppress the diagnostic. @@ -501,17 +523,25 @@ void NullabilityChecker::checkPreStmt(co // function with a _Nonnull return type: // return (NSString * _Nonnull)0; Nullability RetExprTypeLevelNullability = - getNullabilityAnnotation(RetExpr->getType()); + getNullabilityAnnotation(lookThroughImplicitCasts(RetExpr)->getType()); if (Filter.CheckNullReturnedFromNonnull && Nullness == NullConstraint::IsNull && RetExprTypeLevelNullability != Nullability::Nonnull && - RequiredNullability == Nullability::Nonnull) { + RequiredNullability == Nullability::Nonnull && + !InSuppressedMethodFamily) { static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); ExplodedNode *N = C.generateErrorNode(State, &Tag); if (!N) return; - reportBugIfPreconditionHolds(ErrorKind::NilReturnedToNonnull, N, nullptr, C, + + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Null is returned from a " << C.getDeclDescription(D) << + " that is expected to return a non-null value"; + + reportBugIfPreconditionHolds(OS.str(), + ErrorKind::NilReturnedToNonnull, N, nullptr, C, RetExpr); return; } @@ -530,7 +560,14 @@ void NullabilityChecker::checkPreStmt(co RequiredNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull"); ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); - reportBugIfPreconditionHolds(ErrorKind::NullableReturnedToNonnull, N, + + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) << + " that is expected to return a non-null value"; + + reportBugIfPreconditionHolds(OS.str(), + ErrorKind::NullableReturnedToNonnull, N, Region, C); } return; @@ -579,14 +616,21 @@ void NullabilityChecker::checkPreCall(co Nullability ArgExprTypeLevelNullability = getNullabilityAnnotation(ArgExpr->getType()); + unsigned ParamIdx = Param->getFunctionScopeIndex() + 1; + if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && ArgExprTypeLevelNullability != Nullability::Nonnull && RequiredNullability == Nullability::Nonnull) { ExplodedNode *N = C.generateErrorNode(State); if (!N) return; - reportBugIfPreconditionHolds(ErrorKind::NilPassedToNonnull, N, nullptr, C, - ArgExpr); + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Null passed to a callee that requires a non-null " << ParamIdx + << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; + reportBugIfPreconditionHolds(OS.str(), ErrorKind::NilPassedToNonnull, N, + nullptr, C, + ArgExpr, /*SuppressPath=*/false); return; } @@ -605,14 +649,20 @@ void NullabilityChecker::checkPreCall(co if (Filter.CheckNullablePassedToNonnull && RequiredNullability == Nullability::Nonnull) { ExplodedNode *N = C.addTransition(State); - reportBugIfPreconditionHolds(ErrorKind::NullablePassedToNonnull, N, + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Nullable pointer is passed to a callee that requires a non-null " + << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; + reportBugIfPreconditionHolds(OS.str(), + ErrorKind::NullablePassedToNonnull, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } if (Filter.CheckNullableDereferenced && Param->getType()->isReferenceType()) { ExplodedNode *N = C.addTransition(State); - reportBugIfPreconditionHolds(ErrorKind::NullableDereferenced, N, Region, + reportBugIfPreconditionHolds("Nullable pointer is dereferenced", + ErrorKind::NullableDereferenced, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } @@ -981,7 +1031,9 @@ void NullabilityChecker::checkBind(SVal if (!ValueExpr) ValueExpr = S; - reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N, nullptr, C, + reportBugIfPreconditionHolds("Null is assigned to a pointer which is " + "expected to have non-null value", + ErrorKind::NilAssignedToNonnull, N, nullptr, C, ValueExpr); return; } @@ -1003,7 +1055,9 @@ void NullabilityChecker::checkBind(SVal LocNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull"); ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); - reportBugIfPreconditionHolds(ErrorKind::NullableAssignedToNonnull, N, + reportBugIfPreconditionHolds("Nullable pointer is assigned to a pointer " + "which is expected to have non-null value", + ErrorKind::NullableAssignedToNonnull, N, ValueRegion, C); } return; Modified: cfe/tags/checker/checker-278/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp (original) +++ cfe/tags/checker/checker-278/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Fri Feb 5 13:17:16 2016 @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" +#include "clang/Analysis/CFGStmtMap.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -828,8 +829,53 @@ SuppressInlineDefensiveChecksVisitor::Vi // Check if this is inlined defensive checks. const LocationContext *CurLC =Succ->getLocationContext(); const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext(); - if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) + if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) { BR.markInvalid("Suppress IDC", CurLC); + return nullptr; + } + + // Treat defensive checks in function-like macros as if they were an inlined + // defensive check. If the bug location is not in a macro and the + // terminator for the current location is in a macro then suppress the + // warning. + auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>(); + + if (!BugPoint) + return nullptr; + + SourceLocation BugLoc = BugPoint->getStmt()->getLocStart(); + if (BugLoc.isMacroID()) + return nullptr; + + ProgramPoint CurPoint = Succ->getLocation(); + const Stmt *CurTerminatorStmt = nullptr; + if (auto BE = CurPoint.getAs<BlockEdge>()) { + CurTerminatorStmt = BE->getSrc()->getTerminator().getStmt(); + } else if (auto SP = CurPoint.getAs<StmtPoint>()) { + const Stmt *CurStmt = SP->getStmt(); + if (!CurStmt->getLocStart().isMacroID()) + return nullptr; + + CFGStmtMap *Map = CurLC->getAnalysisDeclContext()->getCFGStmtMap(); + CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminator(); + } else { + return nullptr; + } + + if (!CurTerminatorStmt) + return nullptr; + + SourceLocation TerminatorLoc = CurTerminatorStmt->getLocStart(); + if (TerminatorLoc.isMacroID()) { + const SourceManager &SMgr = BRC.getSourceManager(); + std::pair<FileID, unsigned> TLInfo = SMgr.getDecomposedLoc(TerminatorLoc); + SrcMgr::SLocEntry SE = SMgr.getSLocEntry(TLInfo.first); + const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion(); + if (EInfo.isFunctionMacroExpansion()) { + BR.markInvalid("Suppress Macro IDC", CurLC); + return nullptr; + } + } } return nullptr; } Modified: cfe/tags/checker/checker-278/lib/StaticAnalyzer/Core/CheckerContext.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/lib/StaticAnalyzer/Core/CheckerContext.cpp?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/lib/StaticAnalyzer/Core/CheckerContext.cpp (original) +++ cfe/tags/checker/checker-278/lib/StaticAnalyzer/Core/CheckerContext.cpp Fri Feb 5 13:17:16 2016 @@ -35,6 +35,13 @@ StringRef CheckerContext::getCalleeName( return funI->getName(); } +StringRef CheckerContext::getDeclDescription(const Decl *D) { + if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D)) + return "method"; + if (isa<BlockDecl>(D)) + return "anonymous block"; + return "function"; +} bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, StringRef Name) { Modified: cfe/tags/checker/checker-278/test/Analysis/MissingDealloc.m URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/test/Analysis/MissingDealloc.m?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/test/Analysis/MissingDealloc.m (original) +++ cfe/tags/checker/checker-278/test/Analysis/MissingDealloc.m Fri Feb 5 13:17:16 2016 @@ -1,5 +1,6 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc %s -verify -// expected-no-diagnostics +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks %s 2>&1 | FileCheck -check-prefix=CHECK %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc %s 2>&1 | FileCheck -check-prefix=CHECK-ARC -allow-empty '--implicit-check-not=error:' '--implicit-check-not=warning:' %s + typedef signed char BOOL; @protocol NSObject - (BOOL)isEqual:(id)object; @@ -13,23 +14,74 @@ typedef signed char BOOL; typedef struct objc_selector *SEL; -// <rdar://problem/6380411>: 'myproperty' has kind 'assign' and thus the -// assignment through the setter does not perform a release. +//===------------------------------------------------------------------------=== +// Do not warn about missing -dealloc method. Not enough context to know +// whether the ivar is retained or not. -@interface MyObject : NSObject { - id _myproperty; +@interface MissingDeallocWithIvar : NSObject { + NSObject *_ivar; } -@property(assign) id myproperty; @end -@implementation MyObject -@synthesize myproperty=_myproperty; // no-warning -- (void)dealloc { - self.myproperty = 0; - [super dealloc]; +@implementation MissingDeallocWithIvar +@end + +//===------------------------------------------------------------------------=== +// Do not warn about missing -dealloc method. These properties are not +// retained or synthesized. + +@interface MissingDeallocWithIntProperty : NSObject +@property (assign) int ivar; +@end + +@implementation MissingDeallocWithIntProperty +@end + +@interface MissingDeallocWithSELProperty : NSObject +@property (assign) SEL ivar; +@end + +@implementation MissingDeallocWithSELProperty +@end + +//===------------------------------------------------------------------------=== +// Warn about missing -dealloc method. + +@interface MissingDeallocWithCopyProperty : NSObject +@property (copy) NSObject *ivar; +@end + +// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithCopyProperty' lacks a 'dealloc' instance method +@implementation MissingDeallocWithCopyProperty +@end + +@interface MissingDeallocWithRetainProperty : NSObject +@property (retain) NSObject *ivar; +@end + +// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithRetainProperty' lacks a 'dealloc' instance method +@implementation MissingDeallocWithRetainProperty +@end + +@interface MissingDeallocWithIVarAndRetainProperty : NSObject { + NSObject *_ivar2; } +@property (retain) NSObject *ivar1; +@end + +// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithIVarAndRetainProperty' lacks a 'dealloc' instance method +@implementation MissingDeallocWithIVarAndRetainProperty @end +@interface MissingDeallocWithReadOnlyRetainedProperty : NSObject +@property (readonly,retain) NSObject *ivar; +@end + +// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithReadOnlyRetainedProperty' lacks a 'dealloc' instance method +@implementation MissingDeallocWithReadOnlyRetainedProperty +@end + + //===------------------------------------------------------------------------=== // Don't warn about iVars that are selectors. @@ -65,27 +117,6 @@ IBOutlet NSWindow *window; @end //===------------------------------------------------------------------------=== -// <rdar://problem/6380411> -// Was bogus warning: "The '_myproperty' instance variable was not retained by a -// synthesized property but was released in 'dealloc'" - -@interface MyObject_rdar6380411 : NSObject { - id _myproperty; -} -@property(assign) id myproperty; -@end - -@implementation MyObject_rdar6380411 -@synthesize myproperty=_myproperty; -- (void)dealloc { - // Don't claim that myproperty is released since it the property - // has the 'assign' attribute. - self.myproperty = 0; // no-warning - [super dealloc]; -} -@end - -//===------------------------------------------------------------------------=== // PR 3187: http://llvm.org/bugs/show_bug.cgi?id=3187 // - Disable the missing -dealloc check for classes that subclass SenTestCase @@ -112,3 +143,4 @@ IBOutlet NSWindow *window; // do something which uses resourcepath } @end +// CHECK: 4 warnings generated. Modified: cfe/tags/checker/checker-278/test/Analysis/PR2978.m URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/test/Analysis/PR2978.m?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/test/Analysis/PR2978.m (original) +++ cfe/tags/checker/checker-278/test/Analysis/PR2978.m Fri Feb 5 13:17:16 2016 @@ -14,6 +14,7 @@ id _Y; id _Z; id _K; + id _L; id _N; id _M; id _V; @@ -23,6 +24,7 @@ @property(retain) id Y; @property(assign) id Z; @property(assign) id K; +@property(weak) id L; @property(readonly) id N; @property(retain) id M; @property(retain) id V; @@ -33,13 +35,14 @@ @implementation MyClass @synthesize X = _X; -@synthesize Y = _Y; // expected-warning{{The '_Y' instance variable was retained by a synthesized property but wasn't released in 'dealloc'}} -@synthesize Z = _Z; // expected-warning{{The '_Z' instance variable was not retained by a synthesized property but was released in 'dealloc'}} +@synthesize Y = _Y; // expected-warning{{The '_Y' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} +@synthesize Z = _Z; // expected-warning{{The '_Z' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} @synthesize K = _K; -@synthesize N = _N; +@synthesize L = _L; // no-warning +@synthesize N = _N; // expected-warning{{The '_N' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} @synthesize M = _M; @synthesize V = _V; -@synthesize W = _W; // expected-warning{{The '_W' instance variable was retained by a synthesized property but wasn't released in 'dealloc'}} +@synthesize W = _W; // expected-warning{{The '_W' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} -(id) O{ return 0; } -(void) setO:(id)arg { } Modified: cfe/tags/checker/checker-278/test/Analysis/inlining/false-positive-suppression.c URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/test/Analysis/inlining/false-positive-suppression.c?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/test/Analysis/inlining/false-positive-suppression.c (original) +++ cfe/tags/checker/checker-278/test/Analysis/inlining/false-positive-suppression.c Fri Feb 5 13:17:16 2016 @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-config suppress-null-return-paths=false -verify %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify -DSUPPRESSED=1 %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-config avoid-suppressing-null-argument-paths=true -DSUPPRESSED=1 -DNULL_ARGS=1 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-eagerly-assume -analyzer-checker=core -analyzer-config suppress-null-return-paths=false -verify %s +// RUN: %clang_cc1 -analyze -analyzer-eagerly-assume -analyzer-checker=core -verify -DSUPPRESSED=1 %s +// RUN: %clang_cc1 -analyze -analyzer-eagerly-assume -analyzer-checker=core -analyzer-config avoid-suppressing-null-argument-paths=true -DSUPPRESSED=1 -DNULL_ARGS=1 -verify %s int opaquePropertyCheck(void *object); int coin(); @@ -93,6 +93,84 @@ int triggerDivZero () { return 5/y; // expected-warning {{Division by zero}} } +// Treat a function-like macro similarly to an inlined function, so suppress +// warnings along paths resulting from inlined checks. +#define MACRO_WITH_CHECK(a) ( ((a) != 0) ? *a : 17) +void testInlineCheckInMacro(int *p) { + int i = MACRO_WITH_CHECK(p); + (void)i; + + *p = 1; // no-warning +} + +#define MACRO_WITH_NESTED_CHECK(a) ( { int j = MACRO_WITH_CHECK(a); j; } ) +void testInlineCheckInNestedMacro(int *p) { + int i = MACRO_WITH_NESTED_CHECK(p); + (void)i; + + *p = 1; // no-warning +} + +// If there is a check in a macro that is not function-like, don't treat +// it like a function so don't suppress. +#define NON_FUNCTION_MACRO_WITH_CHECK ( ((p) != 0) ? *p : 17) +void testNonFunctionMacro(int *p) { + int i = NON_FUNCTION_MACRO_WITH_CHECK ; + (void)i; + + *p = 1; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} +} + + +// This macro will dereference its argument if the argument is NULL. +#define MACRO_WITH_ERROR(a) ( ((a) != 0) ? 0 : *a) +void testErrorInMacro(int *p) { + int i = MACRO_WITH_ERROR(p); // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} + (void)i; +} + +// Here the check (the "if") is not in a macro, so we should still warn. +#define MACRO_IN_GUARD(a) (!(a)) +void testMacroUsedAsGuard(int *p) { + if (MACRO_IN_GUARD(p)) + *p = 1; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} +} + +// When a nil case split is introduced in a macro and the macro is in a guard, +// we still shouldn't warn. +int isNull(int *p); +int isEqual(int *p, int *q); +#define ISNULL(ptr) ((ptr) == 0 || isNull(ptr)) +#define ISEQUAL(a, b) ((int *)(a) == (int *)(b) || (ISNULL(a) && ISNULL(b)) || isEqual(a,b)) +#define ISNOTEQUAL(a, b) (!ISEQUAL(a, b)) +void testNestedDisjunctiveMacro(int *p, int *q) { + if (ISNOTEQUAL(p,q)) { + *p = 1; // no-warning + *q = 1; // no-warning + } + + *p = 1; // no-warning + *q = 1; // no-warning +} + +void testNestedDisjunctiveMacro2(int *p, int *q) { + if (ISEQUAL(p,q)) { + return; + } + + *p = 1; // no-warning + *q = 1; // no-warning +} + + +// Here the check is entirely in non-macro code even though the code itself +// is a macro argument. +#define MACRO_DO_IT(a) (a) +void testErrorInArgument(int *p) { + int i = MACRO_DO_IT((p ? 0 : *p)); // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}c + (void)i; +} + // -------------------------- // "Suppression suppression" // -------------------------- Modified: cfe/tags/checker/checker-278/test/Analysis/nullability.mm URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/test/Analysis/nullability.mm?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/test/Analysis/nullability.mm (original) +++ cfe/tags/checker/checker-278/test/Analysis/nullability.mm Fri Feb 5 13:17:16 2016 @@ -1,15 +1,28 @@ -// RUN: %clang_cc1 -fobjc-arc -analyze -analyzer-checker=core,nullability -verify %s +// RUN: %clang_cc1 -fblocks -analyze -analyzer-checker=core,nullability -verify %s #define nil 0 #define BOOL int +#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +#define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") + +typedef struct _NSZone NSZone; + @protocol NSObject + (id)alloc; - (id)init; @end +NS_ASSUME_NONNULL_BEGIN @protocol NSCopying +- (id)copyWithZone:(nullable NSZone *)zone; + +@end + +@protocol NSMutableCopying +- (id)mutableCopyWithZone:(nullable NSZone *)zone; @end +NS_ASSUME_NONNULL_END __attribute__((objc_root_class)) @interface @@ -58,16 +71,16 @@ void testBasicRules() { // Make every dereference a different path to avoid sinks after errors. switch (getRandom()) { case 0: { - Dummy &r = *p; // expected-warning {{}} + Dummy &r = *p; // expected-warning {{Nullable pointer is dereferenced}} } break; case 1: { - int b = p->val; // expected-warning {{}} + int b = p->val; // expected-warning {{Nullable pointer is dereferenced}} } break; case 2: { - int stuff = *ptr; // expected-warning {{}} + int stuff = *ptr; // expected-warning {{Nullable pointer is dereferenced}} } break; case 3: - takesNonnull(p); // expected-warning {{}} + takesNonnull(p); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} break; case 4: { Dummy d; @@ -89,11 +102,11 @@ void testBasicRules() { Dummy *q = 0; if (getRandom()) { takesNullable(q); - takesNonnull(q); // expected-warning {{}} + takesNonnull(q); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} } Dummy a; Dummy *_Nonnull nonnull = &a; - nonnull = q; // expected-warning {{}} + nonnull = q; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}} q = &a; takesNullable(q); takesNonnull(q); @@ -106,26 +119,26 @@ void testArgumentTracking(Dummy *_Nonnul Dummy *p = nullable; Dummy *q = nonnull; switch(getRandom()) { - case 1: nonnull = p; break; // expected-warning {{}} + case 1: nonnull = p; break; // expected-warning {{Nullable pointer is assigned to a pointer which is expected to have non-null value}} case 2: p = 0; break; case 3: q = p; break; case 4: testMultiParamChecking(nonnull, nullable, nonnull); break; case 5: testMultiParamChecking(nonnull, nonnull, nonnull); break; - case 6: testMultiParamChecking(nonnull, nullable, nullable); break; // expected-warning {{}} - case 7: testMultiParamChecking(nullable, nullable, nonnull); // expected-warning {{}} - case 8: testMultiParamChecking(nullable, nullable, nullable); // expected-warning {{}} + case 6: testMultiParamChecking(nonnull, nullable, nullable); break; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 3rd parameter}} + case 7: testMultiParamChecking(nullable, nullable, nonnull); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} + case 8: testMultiParamChecking(nullable, nullable, nullable); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} case 9: testMultiParamChecking((Dummy *_Nonnull)0, nullable, nonnull); break; } } Dummy *_Nonnull testNullableReturn(Dummy *_Nullable a) { Dummy *p = a; - return p; // expected-warning {{}} + return p; // expected-warning {{Nullable pointer is returned from a function that is expected to return a non-null value}} } Dummy *_Nonnull testNullReturn() { Dummy *p = 0; - return p; // expected-warning {{}} + return p; // expected-warning {{Null is returned from a function that is expected to return a non-null value}} } void testObjCMessageResultNullability() { @@ -147,20 +160,20 @@ void testObjCMessageResultNullability() break; case 3: shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNullable]; - [o takesNonnull:shouldBeNullable]; // expected-warning {{}} + [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} break; case 4: shouldBeNullable = [eraseNullab(getNonnullTestObject()) returnsNullable]; - [o takesNonnull:shouldBeNullable]; // expected-warning {{}} + [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} break; case 5: shouldBeNullable = [eraseNullab(getUnspecifiedTestObject()) returnsNullable]; - [o takesNonnull:shouldBeNullable]; // expected-warning {{}} + [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} break; case 6: shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNullable]; - [o takesNonnull:shouldBeNullable]; // expected-warning {{}} + [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} break; case 7: { int *shouldBeNonnull = [eraseNullab(getNonnullTestObject()) returnsNonnull]; @@ -189,7 +202,18 @@ Dummy * _Nonnull testDirectCastNilToNonn void testIndirectCastNilToNonnullAndPass() { Dummy *p = (Dummy * _Nonnull)0; // FIXME: Ideally the cast above would suppress this warning. - takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null argument}} + takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} +} + +void testIndirectNilPassToNonnull() { + Dummy *p = 0; + takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} +} + +void testConditionalNilPassToNonnull(Dummy *p) { + if (!p) { + takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} + } } Dummy * _Nonnull testIndirectCastNilToNonnullAndReturn() { @@ -206,7 +230,7 @@ void testInvalidPropagation() { void onlyReportFirstPreconditionViolationOnPath() { Dummy *p = returnsNullable(); - takesNonnull(p); // expected-warning {{}} + takesNonnull(p); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} takesNonnull(p); // No warning. // The first warning was not a sink. The analysis expected to continue. int i = 0; @@ -255,6 +279,14 @@ void inlinedUnspecified(Dummy *p) { if (p) return; } +void testNilReturnWithBlock(Dummy *p) { + p = 0; + Dummy *_Nonnull (^myblock)(void) = ^Dummy *_Nonnull(void) { + return p; // TODO: We should warn in blocks. + }; + myblock(); +} + Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) { switch (getRandom()) { case 1: inlinedNullable(p); break; @@ -279,11 +311,111 @@ Dummy *_Nonnull testDefensiveInlineCheck return p; } -void testObjCARCImplicitZeroInitialization() { - TestObject * _Nonnull implicitlyZeroInitialized; // no-warning - implicitlyZeroInitialized = getNonnullTestObject(); + +@interface SomeClass : NSObject { + int instanceVar; +} +@end + +@implementation SomeClass (MethodReturn) +- (id)initWithSomething:(int)i { + if (self = [super init]) { + instanceVar = i; + } + + return self; +} + +- (TestObject * _Nonnull)testReturnsNullableInNonnullIndirectly { + TestObject *local = getNullableTestObject(); + return local; // expected-warning {{Nullable pointer is returned from a method that is expected to return a non-null value}} } -void testObjCARCExplicitZeroInitialization() { - TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}} +- (TestObject * _Nonnull)testReturnsCastSuppressedNullableInNonnullIndirectly { + TestObject *local = getNullableTestObject(); + return (TestObject * _Nonnull)local; // no-warning } + +- (TestObject * _Nonnull)testReturnsNullableInNonnullWhenPreconditionViolated:(TestObject * _Nonnull) p { + TestObject *local = getNullableTestObject(); + if (!p) // Pre-condition violated here. + return local; // no-warning + else + return p; // no-warning +} +@end + +@interface ClassWithInitializers : NSObject +@end + +@implementation ClassWithInitializers +- (instancetype _Nonnull)initWithNonnullReturnAndSelfCheckingIdiom { + // This defensive check is a common-enough idiom that we filter don't want + // to issue a diagnostic for it, + if (self = [super init]) { + } + + return self; // no-warning +} + +- (instancetype _Nonnull)initWithNonnullReturnAndNilReturnViaLocal { + self = [super init]; + // This leaks, but we're not checking for that here. + + ClassWithInitializers *other = nil; + // False negative. Once we have more subtle suppression of defensive checks in + // initializers we should warn here. + return other; +} +@end + +@interface SubClassWithInitializers : ClassWithInitializers +@end + +@implementation SubClassWithInitializers +// Note: Because this is overridding +// -[ClassWithInitializers initWithNonnullReturnAndSelfCheckingIdiom], +// the return type of this method becomes implicitly id _Nonnull. +- (id)initWithNonnullReturnAndSelfCheckingIdiom { + if (self = [super initWithNonnullReturnAndSelfCheckingIdiom]) { + } + + return self; // no-warning +} + +- (id _Nonnull)initWithNonnullReturnAndSelfCheckingIdiomV2; { + // Another common return-checking idiom + self = [super initWithNonnullReturnAndSelfCheckingIdiom]; + if (!self) { + return nil; // no-warning + } + + return self; +} +@end + +@interface ClassWithCopyWithZone : NSObject<NSCopying,NSMutableCopying> { + id i; +} + +@end + +@implementation ClassWithCopyWithZone +-(id)copyWithZone:(NSZone *)zone { + ClassWithCopyWithZone *newInstance = [[ClassWithCopyWithZone alloc] init]; + if (!newInstance) + return nil; + + newInstance->i = i; + return newInstance; +} + +-(id)mutableCopyWithZone:(NSZone *)zone { + ClassWithCopyWithZone *newInstance = [[ClassWithCopyWithZone alloc] init]; + if (newInstance) { + newInstance->i = i; + } + + return newInstance; +} +@end Modified: cfe/tags/checker/checker-278/test/Analysis/nullability_nullonly.mm URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/test/Analysis/nullability_nullonly.mm?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/test/Analysis/nullability_nullonly.mm (original) +++ cfe/tags/checker/checker-278/test/Analysis/nullability_nullonly.mm Fri Feb 5 13:17:16 2016 @@ -1,4 +1,20 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull -verify %s +// RUN: %clang_cc1 -analyze -fobjc-arc -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull -verify %s + +#define nil 0 +#define BOOL int + +@protocol NSObject ++ (id)alloc; +- (id)init; +@end + +@protocol NSCopying +@end + +__attribute__((objc_root_class)) +@interface +NSObject<NSObject> +@end int getRandom(); @@ -15,18 +31,18 @@ void testBasicRules() { Dummy *q = 0; if (getRandom()) { takesNullable(q); - takesNonnull(q); // expected-warning {{}} + takesNonnull(q); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} } } Dummy *_Nonnull testNullReturn() { Dummy *p = 0; - return p; // expected-warning {{}} + return p; // expected-warning {{Null is returned from a function that is expected to return a non-null value}} } void onlyReportFirstPreconditionViolationOnPath() { Dummy *p = 0; - takesNonnull(p); // expected-warning {{}} + takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} takesNonnull(p); // No warning. // Passing null to nonnull is a sink. Stop the analysis. int i = 0; @@ -85,3 +101,61 @@ Dummy *_Nonnull testDefensiveInlineCheck takesNonnull(p); return p; } + +@interface TestObject : NSObject +@end + +TestObject *_Nonnull getNonnullTestObject(); + +void testObjCARCImplicitZeroInitialization() { + TestObject * _Nonnull implicitlyZeroInitialized; // no-warning + implicitlyZeroInitialized = getNonnullTestObject(); +} + +void testObjCARCExplicitZeroInitialization() { + TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}} +} + +// Under ARC, returned expressions of ObjC objects types are are implicitly +// cast to _Nonnull when the functions return type is _Nonnull, so make +// sure this doesn't implicit cast doesn't suppress a legitimate warning. +TestObject * _Nonnull returnsNilObjCInstanceIndirectly() { + TestObject *local = 0; + return local; // expected-warning {{Null is returned from a function that is expected to return a non-null value}} +} + +TestObject * _Nonnull returnsNilObjCInstanceIndirectlyWithSupressingCast() { + TestObject *local = 0; + return (TestObject * _Nonnull)local; // no-warning +} + +TestObject * _Nonnull returnsNilObjCInstanceDirectly() { + return nil; // expected-warning {{Null is returned from a function that is expected to return a non-null value}} +} + +TestObject * _Nonnull returnsNilObjCInstanceDirectlyWithSuppressingCast() { + return (TestObject * _Nonnull)nil; // no-warning +} + +@interface SomeClass : NSObject +@end + +@implementation SomeClass (MethodReturn) +- (SomeClass * _Nonnull)testReturnsNilInNonnull { + SomeClass *local = nil; + return local; // expected-warning {{Null is returned from a method that is expected to return a non-null value}} +} + +- (SomeClass * _Nonnull)testReturnsCastSuppressedNilInNonnull { + SomeClass *local = nil; + return (SomeClass * _Nonnull)local; // no-warning +} + +- (SomeClass * _Nonnull)testReturnsNilInNonnullWhenPreconditionViolated:(SomeClass * _Nonnull) p { + SomeClass *local = nil; + if (!p) // Pre-condition violated here. + return local; // no-warning + else + return p; // no-warning +} +@end Modified: cfe/tags/checker/checker-278/test/Analysis/properties.m URL: http://llvm.org/viewvc/llvm-project/cfe/tags/checker/checker-278/test/Analysis/properties.m?rev=259905&r1=259904&r2=259905&view=diff ============================================================================== --- cfe/tags/checker/checker-278/test/Analysis/properties.m (original) +++ cfe/tags/checker/checker-278/test/Analysis/properties.m Fri Feb 5 13:17:16 2016 @@ -211,6 +211,33 @@ void testConsistencyAssign(Person *p) { clang_analyzer_eval(p.friend == origFriend); // expected-warning{{UNKNOWN}} } +@interface ClassWithShadowedReadWriteProperty { + int _f; +} +@property (readonly) int someProp; +@end + +@interface ClassWithShadowedReadWriteProperty () +@property (readwrite) int someProp; +@end + +@implementation ClassWithShadowedReadWriteProperty +- (void)testSynthesisForShadowedReadWriteProperties; { + clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}} + + _f = 1; + + // Read of shadowed property should not invalidate receiver. + (void)self.someProp; + clang_analyzer_eval(_f == 1); // expected-warning{{TRUE}} + + _f = 2; + // Call to getter of shadowed property should not invalidate receiver. + (void)[self someProp]; + clang_analyzer_eval(_f == 2); // expected-warning{{TRUE}} +} +@end + #if !__has_feature(objc_arc) void testOverrelease(Person *p, int coin) { switch (coin) { _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits