diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers')
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 { |

