[llvm-branch-commits] [cfe-tag] r259905 - [analyzer] checker-277: Pull in nullability fixes and dealloc checker.
Devin Coughlin via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Feb 5 11:17:16 PST 2016
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.
- at interface MyObject : NSObject {
- id _myproperty;
+ at interface MissingDeallocWithIvar : NSObject {
+ NSObject *_ivar;
}
- at property(assign) id myproperty;
@end
- at implementation MyObject
- at synthesize myproperty=_myproperty; // no-warning
-- (void)dealloc {
- self.myproperty = 0;
- [super dealloc];
+ at implementation MissingDeallocWithIvar
+ at end
+
+//===------------------------------------------------------------------------===
+// Do not warn about missing -dealloc method. These properties are not
+// retained or synthesized.
+
+ at interface MissingDeallocWithIntProperty : NSObject
+ at property (assign) int ivar;
+ at end
+
+ at implementation MissingDeallocWithIntProperty
+ at end
+
+ at interface MissingDeallocWithSELProperty : NSObject
+ at property (assign) SEL ivar;
+ at end
+
+ at implementation MissingDeallocWithSELProperty
+ at end
+
+//===------------------------------------------------------------------------===
+// Warn about missing -dealloc method.
+
+ at interface MissingDeallocWithCopyProperty : NSObject
+ at property (copy) NSObject *ivar;
+ at end
+
+// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithCopyProperty' lacks a 'dealloc' instance method
+ at implementation MissingDeallocWithCopyProperty
+ at end
+
+ at interface MissingDeallocWithRetainProperty : NSObject
+ at property (retain) NSObject *ivar;
+ at end
+
+// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithRetainProperty' lacks a 'dealloc' instance method
+ at implementation MissingDeallocWithRetainProperty
+ at end
+
+ at interface MissingDeallocWithIVarAndRetainProperty : NSObject {
+ NSObject *_ivar2;
}
+ at property (retain) NSObject *ivar1;
+ at end
+
+// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithIVarAndRetainProperty' lacks a 'dealloc' instance method
+ at implementation MissingDeallocWithIVarAndRetainProperty
@end
+ at interface MissingDeallocWithReadOnlyRetainedProperty : NSObject
+ at property (readonly,retain) NSObject *ivar;
+ at end
+
+// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithReadOnlyRetainedProperty' lacks a 'dealloc' instance method
+ at implementation MissingDeallocWithReadOnlyRetainedProperty
+ at 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'"
-
- at interface MyObject_rdar6380411 : NSObject {
- id _myproperty;
-}
- at property(assign) id myproperty;
- at end
-
- at implementation MyObject_rdar6380411
- at 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];
-}
- at 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;
+ at 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;
- at synthesize Y = _Y; // expected-warning{{The '_Y' instance variable was retained by a synthesized property but wasn't released in 'dealloc'}}
- at synthesize Z = _Z; // expected-warning{{The '_Z' instance variable was not retained by a synthesized property but was released in 'dealloc'}}
+ at synthesize Y = _Y; // expected-warning{{The '_Y' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}}
+ at 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;
- at synthesize N = _N;
+ at synthesize L = _L; // no-warning
+ at 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;
- at synthesize W = _W; // expected-warning{{The '_W' instance variable was retained by a synthesized property but wasn't released in 'dealloc'}}
+ at 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;
+
+ at end
+
+ at 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();
+
+ at interface SomeClass : NSObject {
+ int instanceVar;
+}
+ at end
+
+ at 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
+}
+ at end
+
+ at interface ClassWithInitializers : NSObject
+ at end
+
+ at 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;
+}
+ at end
+
+ at interface SubClassWithInitializers : ClassWithInitializers
+ at end
+
+ at 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;
+}
+ at end
+
+ at interface ClassWithCopyWithZone : NSObject<NSCopying,NSMutableCopying> {
+ id i;
+}
+
+ at end
+
+ at 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;
+}
+ at 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
+
+ at protocol NSObject
++ (id)alloc;
+- (id)init;
+ at end
+
+ at protocol NSCopying
+ at end
+
+__attribute__((objc_root_class))
+ at interface
+NSObject<NSObject>
+ at 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;
}
+
+ at interface TestObject : NSObject
+ at 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
+}
+
+ at interface SomeClass : NSObject
+ at end
+
+ at 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
+}
+ at 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}}
}
+ at interface ClassWithShadowedReadWriteProperty {
+ int _f;
+}
+ at property (readonly) int someProp;
+ at end
+
+ at interface ClassWithShadowedReadWriteProperty ()
+ at property (readwrite) int someProp;
+ at end
+
+ at 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}}
+}
+ at end
+
#if !__has_feature(objc_arc)
void testOverrelease(Person *p, int coin) {
switch (coin) {
More information about the llvm-branch-commits
mailing list