summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer/Checkers
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp51
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp54
3 files changed, 80 insertions, 28 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
index b55ea2a116e..18de1de7845 100644
--- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
@@ -1308,10 +1308,6 @@ RetainCountChecker::processLeaks(ProgramStateRef state,
return N;
}
-static bool isISLObjectRef(QualType Ty) {
- return StringRef(Ty.getAsString()).startswith("isl_");
-}
-
void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
if (!Ctx.inTopFrame())
return;
@@ -1333,13 +1329,14 @@ void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
QualType Ty = Param->getType();
const ArgEffect *AE = CalleeSideArgEffects.lookup(idx);
- if (AE && AE->getKind() == DecRef && isISLObjectRef(Ty)) {
- state = setRefBinding(
- state, Sym, RefVal::makeOwned(ObjKind::Generalized, Ty));
- } else if (isISLObjectRef(Ty)) {
- state = setRefBinding(
- state, Sym,
- RefVal::makeNotOwned(ObjKind::Generalized, Ty));
+ if (AE) {
+ ObjKind K = AE->getObjKind();
+ if (K == ObjKind::Generalized || K == ObjKind::OS ||
+ (TrackNSCFStartParam && (K == ObjKind::ObjC || K == ObjKind::CF))) {
+ RefVal NewVal = AE->getKind() == DecRef ? RefVal::makeOwned(K, Ty)
+ : RefVal::makeNotOwned(K, Ty);
+ state = setRefBinding(state, Sym, NewVal);
+ }
}
}
@@ -1463,29 +1460,37 @@ bool ento::shouldRegisterRetainCountBase(const LangOptions &LO) {
return true;
}
+// FIXME: remove this, hack for backwards compatibility:
+// it should be possible to enable the NS/CF retain count checker as
+// osx.cocoa.RetainCount, and it should be possible to disable
+// osx.OSObjectRetainCount using osx.cocoa.RetainCount:CheckOSObject=false.
+static bool getOption(AnalyzerOptions &Options,
+ StringRef Postfix,
+ StringRef Value) {
+ auto I = Options.Config.find(
+ (StringRef("osx.cocoa.RetainCount:") + Postfix).str());
+ if (I != Options.Config.end())
+ return I->getValue() == Value;
+ return false;
+}
+
void ento::registerRetainCountChecker(CheckerManager &Mgr) {
auto *Chk = Mgr.getChecker<RetainCountChecker>();
Chk->TrackObjCAndCFObjects = true;
+ Chk->TrackNSCFStartParam = getOption(Mgr.getAnalyzerOptions(),
+ "TrackNSCFStartParam",
+ "true");
}
bool ento::shouldRegisterRetainCountChecker(const LangOptions &LO) {
return true;
}
-// FIXME: remove this, hack for backwards compatibility:
-// it should be possible to enable the NS/CF retain count checker as
-// osx.cocoa.RetainCount, and it should be possible to disable
-// osx.OSObjectRetainCount using osx.cocoa.RetainCount:CheckOSObject=false.
-static bool hasPrevCheckOSObjectOptionDisabled(AnalyzerOptions &Options) {
- auto I = Options.Config.find("osx.cocoa.RetainCount:CheckOSObject");
- if (I != Options.Config.end())
- return I->getValue() == "false";
- return false;
-}
-
void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) {
auto *Chk = Mgr.getChecker<RetainCountChecker>();
- if (!hasPrevCheckOSObjectOptionDisabled(Mgr.getAnalyzerOptions()))
+ if (!getOption(Mgr.getAnalyzerOptions(),
+ "CheckOSObject",
+ "false"))
Chk->TrackOSObjects = true;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
index 775cd21851d..506ece1e578 100644
--- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
+++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
@@ -272,6 +272,9 @@ public:
/// Track sublcasses of OSObject.
bool TrackOSObjects = false;
+ /// Track initial parameters (for the entry point) for NS/CF objects.
+ bool TrackNSCFStartParam = false;
+
RetainCountChecker() {};
RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const {
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
index dcb5ca85558..8e13ee35f3c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -418,6 +418,38 @@ annotateConsumedSummaryMismatch(const ExplodedNode *N,
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
+/// Annotate the parameter at the analysis entry point.
+static std::shared_ptr<PathDiagnosticEventPiece>
+annotateStartParameter(const ExplodedNode *N, SymbolRef Sym,
+ const SourceManager &SM) {
+ auto PP = N->getLocationAs<BlockEdge>();
+ if (!PP)
+ return nullptr;
+
+ const CFGBlock *Src = PP->getSrc();
+ const RefVal *CurrT = getRefBinding(N->getState(), Sym);
+
+ if (&Src->getParent()->getEntry() != Src || !CurrT ||
+ getRefBinding(N->getFirstPred()->getState(), Sym))
+ return nullptr;
+
+ const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion());
+ const auto *PVD = cast<ParmVarDecl>(VR->getDecl());
+ PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM);
+
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ os << "Parameter '" << PVD->getNameAsString()
+ << "' starts at +";
+ if (CurrT->getCount() == 1) {
+ os << "1, as it is marked as consuming";
+ } else {
+ assert(CurrT->getCount() == 0);
+ os << "0";
+ }
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+}
+
std::shared_ptr<PathDiagnosticPiece>
RefCountReportVisitor::VisitNode(const ExplodedNode *N,
BugReporterContext &BRC, BugReport &BR) {
@@ -435,6 +467,9 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N,
if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
return PD;
+ if (auto PD = annotateStartParameter(N, Sym, SM))
+ return PD;
+
// FIXME: We will eventually need to handle non-statement-based events
// (__attribute__((cleanup))).
if (!N->getLocation().getAs<StmtPoint>())
@@ -673,7 +708,7 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
if (AllocationNodeInCurrentOrParentContext &&
AllocationNodeInCurrentOrParentContext->getLocationContext() !=
- LeakContext)
+ LeakContext)
FirstBinding = nullptr;
return AllocationInfo(AllocationNodeInCurrentOrParentContext,
@@ -757,10 +792,19 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
}
} else {
const FunctionDecl *FD = cast<FunctionDecl>(D);
- os << "whose name ('" << *FD
- << "') does not contain 'Copy' or 'Create'. This violates the naming"
- " convention rules given in the Memory Management Guide for Core"
- " Foundation";
+ ObjKind K = RV->getObjKind();
+ if (K == ObjKind::ObjC || K == ObjKind::CF) {
+ os << "whose name ('" << *FD
+ << "') does not contain 'Copy' or 'Create'. This violates the "
+ "naming"
+ " convention rules given in the Memory Management Guide for "
+ "Core"
+ " Foundation";
+ } else if (RV->getObjKind() == ObjKind::OS) {
+ std::string FuncName = FD->getNameAsString();
+ os << "whose name ('" << FuncName
+ << "') starts with '" << StringRef(FuncName).substr(0, 3) << "'";
+ }
}
}
} else {
OpenPOWER on IntegriCloud