10b57cec5SDimitry Andric //=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- C++ -*-==//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
9349cc55cSDimitry Andric // This file defines a CheckNSError, a flow-insensitive check
100b57cec5SDimitry Andric // that determines if an Objective-C class interface correctly returns
110b57cec5SDimitry Andric // a non-void return type.
120b57cec5SDimitry Andric //
130b57cec5SDimitry Andric // File under feature request PR 2600.
140b57cec5SDimitry Andric //
150b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
160b57cec5SDimitry Andric
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
180b57cec5SDimitry Andric #include "clang/AST/Decl.h"
190b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
250b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
260b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
27bdd1243dSDimitry Andric #include <optional>
280b57cec5SDimitry Andric
290b57cec5SDimitry Andric using namespace clang;
300b57cec5SDimitry Andric using namespace ento;
310b57cec5SDimitry Andric
320b57cec5SDimitry Andric static bool IsNSError(QualType T, IdentifierInfo *II);
330b57cec5SDimitry Andric static bool IsCFError(QualType T, IdentifierInfo *II);
340b57cec5SDimitry Andric
350b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
360b57cec5SDimitry Andric // NSErrorMethodChecker
370b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
380b57cec5SDimitry Andric
390b57cec5SDimitry Andric namespace {
400b57cec5SDimitry Andric class NSErrorMethodChecker
410b57cec5SDimitry Andric : public Checker< check::ASTDecl<ObjCMethodDecl> > {
42*5f757f3fSDimitry Andric mutable IdentifierInfo *II = nullptr;
430b57cec5SDimitry Andric
440b57cec5SDimitry Andric public:
45*5f757f3fSDimitry Andric NSErrorMethodChecker() = default;
460b57cec5SDimitry Andric
470b57cec5SDimitry Andric void checkASTDecl(const ObjCMethodDecl *D,
480b57cec5SDimitry Andric AnalysisManager &mgr, BugReporter &BR) const;
490b57cec5SDimitry Andric };
500b57cec5SDimitry Andric }
510b57cec5SDimitry Andric
checkASTDecl(const ObjCMethodDecl * D,AnalysisManager & mgr,BugReporter & BR) const520b57cec5SDimitry Andric void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
530b57cec5SDimitry Andric AnalysisManager &mgr,
540b57cec5SDimitry Andric BugReporter &BR) const {
550b57cec5SDimitry Andric if (!D->isThisDeclarationADefinition())
560b57cec5SDimitry Andric return;
570b57cec5SDimitry Andric if (!D->getReturnType()->isVoidType())
580b57cec5SDimitry Andric return;
590b57cec5SDimitry Andric
600b57cec5SDimitry Andric if (!II)
610b57cec5SDimitry Andric II = &D->getASTContext().Idents.get("NSError");
620b57cec5SDimitry Andric
630b57cec5SDimitry Andric bool hasNSError = false;
640b57cec5SDimitry Andric for (const auto *I : D->parameters()) {
650b57cec5SDimitry Andric if (IsNSError(I->getType(), II)) {
660b57cec5SDimitry Andric hasNSError = true;
670b57cec5SDimitry Andric break;
680b57cec5SDimitry Andric }
690b57cec5SDimitry Andric }
700b57cec5SDimitry Andric
710b57cec5SDimitry Andric if (hasNSError) {
720b57cec5SDimitry Andric const char *err = "Method accepting NSError** "
730b57cec5SDimitry Andric "should have a non-void return value to indicate whether or not an "
740b57cec5SDimitry Andric "error occurred";
750b57cec5SDimitry Andric PathDiagnosticLocation L =
760b57cec5SDimitry Andric PathDiagnosticLocation::create(D, BR.getSourceManager());
770b57cec5SDimitry Andric BR.EmitBasicReport(D, this, "Bad return type when passing NSError**",
780b57cec5SDimitry Andric "Coding conventions (Apple)", err, L);
790b57cec5SDimitry Andric }
800b57cec5SDimitry Andric }
810b57cec5SDimitry Andric
820b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
830b57cec5SDimitry Andric // CFErrorFunctionChecker
840b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
850b57cec5SDimitry Andric
860b57cec5SDimitry Andric namespace {
870b57cec5SDimitry Andric class CFErrorFunctionChecker
880b57cec5SDimitry Andric : public Checker< check::ASTDecl<FunctionDecl> > {
890b57cec5SDimitry Andric mutable IdentifierInfo *II;
900b57cec5SDimitry Andric
910b57cec5SDimitry Andric public:
CFErrorFunctionChecker()920b57cec5SDimitry Andric CFErrorFunctionChecker() : II(nullptr) {}
930b57cec5SDimitry Andric
940b57cec5SDimitry Andric void checkASTDecl(const FunctionDecl *D,
950b57cec5SDimitry Andric AnalysisManager &mgr, BugReporter &BR) const;
960b57cec5SDimitry Andric };
970b57cec5SDimitry Andric }
980b57cec5SDimitry Andric
hasReservedReturnType(const FunctionDecl * D)995ffd83dbSDimitry Andric static bool hasReservedReturnType(const FunctionDecl *D) {
1005ffd83dbSDimitry Andric if (isa<CXXConstructorDecl>(D))
1015ffd83dbSDimitry Andric return true;
1025ffd83dbSDimitry Andric
1035ffd83dbSDimitry Andric // operators delete and delete[] are required to have 'void' return type
1045ffd83dbSDimitry Andric auto OperatorKind = D->getOverloadedOperator();
1055ffd83dbSDimitry Andric return OperatorKind == OO_Delete || OperatorKind == OO_Array_Delete;
1065ffd83dbSDimitry Andric }
1075ffd83dbSDimitry Andric
checkASTDecl(const FunctionDecl * D,AnalysisManager & mgr,BugReporter & BR) const1080b57cec5SDimitry Andric void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
1090b57cec5SDimitry Andric AnalysisManager &mgr,
1100b57cec5SDimitry Andric BugReporter &BR) const {
1110b57cec5SDimitry Andric if (!D->doesThisDeclarationHaveABody())
1120b57cec5SDimitry Andric return;
1130b57cec5SDimitry Andric if (!D->getReturnType()->isVoidType())
1140b57cec5SDimitry Andric return;
1155ffd83dbSDimitry Andric if (hasReservedReturnType(D))
1165ffd83dbSDimitry Andric return;
1170b57cec5SDimitry Andric
1180b57cec5SDimitry Andric if (!II)
1190b57cec5SDimitry Andric II = &D->getASTContext().Idents.get("CFErrorRef");
1200b57cec5SDimitry Andric
1210b57cec5SDimitry Andric bool hasCFError = false;
122bdd1243dSDimitry Andric for (auto *I : D->parameters()) {
1230b57cec5SDimitry Andric if (IsCFError(I->getType(), II)) {
1240b57cec5SDimitry Andric hasCFError = true;
1250b57cec5SDimitry Andric break;
1260b57cec5SDimitry Andric }
1270b57cec5SDimitry Andric }
1280b57cec5SDimitry Andric
1290b57cec5SDimitry Andric if (hasCFError) {
1300b57cec5SDimitry Andric const char *err = "Function accepting CFErrorRef* "
1310b57cec5SDimitry Andric "should have a non-void return value to indicate whether or not an "
1320b57cec5SDimitry Andric "error occurred";
1330b57cec5SDimitry Andric PathDiagnosticLocation L =
1340b57cec5SDimitry Andric PathDiagnosticLocation::create(D, BR.getSourceManager());
1350b57cec5SDimitry Andric BR.EmitBasicReport(D, this, "Bad return type when passing CFErrorRef*",
1360b57cec5SDimitry Andric "Coding conventions (Apple)", err, L);
1370b57cec5SDimitry Andric }
1380b57cec5SDimitry Andric }
1390b57cec5SDimitry Andric
1400b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1410b57cec5SDimitry Andric // NSOrCFErrorDerefChecker
1420b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1430b57cec5SDimitry Andric
1440b57cec5SDimitry Andric namespace {
1450b57cec5SDimitry Andric
1460b57cec5SDimitry Andric class NSErrorDerefBug : public BugType {
1470b57cec5SDimitry Andric public:
NSErrorDerefBug(const CheckerNameRef Checker)1485ffd83dbSDimitry Andric NSErrorDerefBug(const CheckerNameRef Checker)
1490b57cec5SDimitry Andric : BugType(Checker, "NSError** null dereference",
1500b57cec5SDimitry Andric "Coding conventions (Apple)") {}
1510b57cec5SDimitry Andric };
1520b57cec5SDimitry Andric
1530b57cec5SDimitry Andric class CFErrorDerefBug : public BugType {
1540b57cec5SDimitry Andric public:
CFErrorDerefBug(const CheckerNameRef Checker)1555ffd83dbSDimitry Andric CFErrorDerefBug(const CheckerNameRef Checker)
1560b57cec5SDimitry Andric : BugType(Checker, "CFErrorRef* null dereference",
1570b57cec5SDimitry Andric "Coding conventions (Apple)") {}
1580b57cec5SDimitry Andric };
1590b57cec5SDimitry Andric
1600b57cec5SDimitry Andric }
1610b57cec5SDimitry Andric
1620b57cec5SDimitry Andric namespace {
1630b57cec5SDimitry Andric class NSOrCFErrorDerefChecker
1640b57cec5SDimitry Andric : public Checker< check::Location,
1650b57cec5SDimitry Andric check::Event<ImplicitNullDerefEvent> > {
1660b57cec5SDimitry Andric mutable IdentifierInfo *NSErrorII, *CFErrorII;
1670b57cec5SDimitry Andric mutable std::unique_ptr<NSErrorDerefBug> NSBT;
1680b57cec5SDimitry Andric mutable std::unique_ptr<CFErrorDerefBug> CFBT;
1690b57cec5SDimitry Andric public:
17081ad6265SDimitry Andric bool ShouldCheckNSError = false, ShouldCheckCFError = false;
1715ffd83dbSDimitry Andric CheckerNameRef NSErrorName, CFErrorName;
NSOrCFErrorDerefChecker()1725ffd83dbSDimitry Andric NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr) {}
1730b57cec5SDimitry Andric
1740b57cec5SDimitry Andric void checkLocation(SVal loc, bool isLoad, const Stmt *S,
1750b57cec5SDimitry Andric CheckerContext &C) const;
1760b57cec5SDimitry Andric void checkEvent(ImplicitNullDerefEvent event) const;
1770b57cec5SDimitry Andric };
1780b57cec5SDimitry Andric }
1790b57cec5SDimitry Andric
1800b57cec5SDimitry Andric typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag;
REGISTER_TRAIT_WITH_PROGRAMSTATE(NSErrorOut,ErrorOutFlag)1810b57cec5SDimitry Andric REGISTER_TRAIT_WITH_PROGRAMSTATE(NSErrorOut, ErrorOutFlag)
1820b57cec5SDimitry Andric REGISTER_TRAIT_WITH_PROGRAMSTATE(CFErrorOut, ErrorOutFlag)
1830b57cec5SDimitry Andric
1840b57cec5SDimitry Andric template <typename T>
1850b57cec5SDimitry Andric static bool hasFlag(SVal val, ProgramStateRef state) {
1860b57cec5SDimitry Andric if (SymbolRef sym = val.getAsSymbol())
1870b57cec5SDimitry Andric if (const unsigned *attachedFlags = state->get<T>(sym))
1880b57cec5SDimitry Andric return *attachedFlags;
1890b57cec5SDimitry Andric return false;
1900b57cec5SDimitry Andric }
1910b57cec5SDimitry Andric
1920b57cec5SDimitry Andric template <typename T>
setFlag(ProgramStateRef state,SVal val,CheckerContext & C)1930b57cec5SDimitry Andric static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) {
1940b57cec5SDimitry Andric // We tag the symbol that the SVal wraps.
1950b57cec5SDimitry Andric if (SymbolRef sym = val.getAsSymbol())
1960b57cec5SDimitry Andric C.addTransition(state->set<T>(sym, true));
1970b57cec5SDimitry Andric }
1980b57cec5SDimitry Andric
parameterTypeFromSVal(SVal val,CheckerContext & C)1990b57cec5SDimitry Andric static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
2000b57cec5SDimitry Andric const StackFrameContext * SFC = C.getStackFrame();
201bdd1243dSDimitry Andric if (std::optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) {
2020b57cec5SDimitry Andric const MemRegion* R = X->getRegion();
2030b57cec5SDimitry Andric if (const VarRegion *VR = R->getAs<VarRegion>())
2040b57cec5SDimitry Andric if (const StackArgumentsSpaceRegion *
2050b57cec5SDimitry Andric stackReg = dyn_cast<StackArgumentsSpaceRegion>(VR->getMemorySpace()))
2060b57cec5SDimitry Andric if (stackReg->getStackFrame() == SFC)
2070b57cec5SDimitry Andric return VR->getValueType();
2080b57cec5SDimitry Andric }
2090b57cec5SDimitry Andric
2100b57cec5SDimitry Andric return QualType();
2110b57cec5SDimitry Andric }
2120b57cec5SDimitry Andric
checkLocation(SVal loc,bool isLoad,const Stmt * S,CheckerContext & C) const2130b57cec5SDimitry Andric void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
2140b57cec5SDimitry Andric const Stmt *S,
2150b57cec5SDimitry Andric CheckerContext &C) const {
2160b57cec5SDimitry Andric if (!isLoad)
2170b57cec5SDimitry Andric return;
21881ad6265SDimitry Andric if (loc.isUndef() || !isa<Loc>(loc))
2190b57cec5SDimitry Andric return;
2200b57cec5SDimitry Andric
2210b57cec5SDimitry Andric ASTContext &Ctx = C.getASTContext();
2220b57cec5SDimitry Andric ProgramStateRef state = C.getState();
2230b57cec5SDimitry Andric
2240b57cec5SDimitry Andric // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting
2250b57cec5SDimitry Andric // SVal so that we can later check it when handling the
2260b57cec5SDimitry Andric // ImplicitNullDerefEvent event.
2270b57cec5SDimitry Andric // FIXME: Cumbersome! Maybe add hook at construction of SVals at start of
2280b57cec5SDimitry Andric // function ?
2290b57cec5SDimitry Andric
2300b57cec5SDimitry Andric QualType parmT = parameterTypeFromSVal(loc, C);
2310b57cec5SDimitry Andric if (parmT.isNull())
2320b57cec5SDimitry Andric return;
2330b57cec5SDimitry Andric
2340b57cec5SDimitry Andric if (!NSErrorII)
2350b57cec5SDimitry Andric NSErrorII = &Ctx.Idents.get("NSError");
2360b57cec5SDimitry Andric if (!CFErrorII)
2370b57cec5SDimitry Andric CFErrorII = &Ctx.Idents.get("CFErrorRef");
2380b57cec5SDimitry Andric
2390b57cec5SDimitry Andric if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) {
2400b57cec5SDimitry Andric setFlag<NSErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
2410b57cec5SDimitry Andric return;
2420b57cec5SDimitry Andric }
2430b57cec5SDimitry Andric
2440b57cec5SDimitry Andric if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) {
2450b57cec5SDimitry Andric setFlag<CFErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
2460b57cec5SDimitry Andric return;
2470b57cec5SDimitry Andric }
2480b57cec5SDimitry Andric }
2490b57cec5SDimitry Andric
checkEvent(ImplicitNullDerefEvent event) const2500b57cec5SDimitry Andric void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
2510b57cec5SDimitry Andric if (event.IsLoad)
2520b57cec5SDimitry Andric return;
2530b57cec5SDimitry Andric
2540b57cec5SDimitry Andric SVal loc = event.Location;
2550b57cec5SDimitry Andric ProgramStateRef state = event.SinkNode->getState();
2560b57cec5SDimitry Andric BugReporter &BR = *event.BR;
2570b57cec5SDimitry Andric
2580b57cec5SDimitry Andric bool isNSError = hasFlag<NSErrorOut>(loc, state);
2590b57cec5SDimitry Andric bool isCFError = false;
2600b57cec5SDimitry Andric if (!isNSError)
2610b57cec5SDimitry Andric isCFError = hasFlag<CFErrorOut>(loc, state);
2620b57cec5SDimitry Andric
2630b57cec5SDimitry Andric if (!(isNSError || isCFError))
2640b57cec5SDimitry Andric return;
2650b57cec5SDimitry Andric
2660b57cec5SDimitry Andric // Storing to possible null NSError/CFErrorRef out parameter.
2670b57cec5SDimitry Andric SmallString<128> Buf;
2680b57cec5SDimitry Andric llvm::raw_svector_ostream os(Buf);
2690b57cec5SDimitry Andric
2700b57cec5SDimitry Andric os << "Potential null dereference. According to coding standards ";
2710b57cec5SDimitry Andric os << (isNSError
2720b57cec5SDimitry Andric ? "in 'Creating and Returning NSError Objects' the parameter"
2730b57cec5SDimitry Andric : "documented in CoreFoundation/CFError.h the parameter");
2740b57cec5SDimitry Andric
2750b57cec5SDimitry Andric os << " may be null";
2760b57cec5SDimitry Andric
2770b57cec5SDimitry Andric BugType *bug = nullptr;
2780b57cec5SDimitry Andric if (isNSError) {
2790b57cec5SDimitry Andric if (!NSBT)
2805ffd83dbSDimitry Andric NSBT.reset(new NSErrorDerefBug(NSErrorName));
2810b57cec5SDimitry Andric bug = NSBT.get();
2820b57cec5SDimitry Andric }
2830b57cec5SDimitry Andric else {
2840b57cec5SDimitry Andric if (!CFBT)
2855ffd83dbSDimitry Andric CFBT.reset(new CFErrorDerefBug(CFErrorName));
2860b57cec5SDimitry Andric bug = CFBT.get();
2870b57cec5SDimitry Andric }
288a7dea167SDimitry Andric BR.emitReport(
289a7dea167SDimitry Andric std::make_unique<PathSensitiveBugReport>(*bug, os.str(), event.SinkNode));
2900b57cec5SDimitry Andric }
2910b57cec5SDimitry Andric
IsNSError(QualType T,IdentifierInfo * II)2920b57cec5SDimitry Andric static bool IsNSError(QualType T, IdentifierInfo *II) {
2930b57cec5SDimitry Andric
2940b57cec5SDimitry Andric const PointerType* PPT = T->getAs<PointerType>();
2950b57cec5SDimitry Andric if (!PPT)
2960b57cec5SDimitry Andric return false;
2970b57cec5SDimitry Andric
2980b57cec5SDimitry Andric const ObjCObjectPointerType* PT =
2990b57cec5SDimitry Andric PPT->getPointeeType()->getAs<ObjCObjectPointerType>();
3000b57cec5SDimitry Andric
3010b57cec5SDimitry Andric if (!PT)
3020b57cec5SDimitry Andric return false;
3030b57cec5SDimitry Andric
3040b57cec5SDimitry Andric const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
3050b57cec5SDimitry Andric
3060b57cec5SDimitry Andric // FIXME: Can ID ever be NULL?
3070b57cec5SDimitry Andric if (ID)
3080b57cec5SDimitry Andric return II == ID->getIdentifier();
3090b57cec5SDimitry Andric
3100b57cec5SDimitry Andric return false;
3110b57cec5SDimitry Andric }
3120b57cec5SDimitry Andric
IsCFError(QualType T,IdentifierInfo * II)3130b57cec5SDimitry Andric static bool IsCFError(QualType T, IdentifierInfo *II) {
3140b57cec5SDimitry Andric const PointerType* PPT = T->getAs<PointerType>();
3150b57cec5SDimitry Andric if (!PPT) return false;
3160b57cec5SDimitry Andric
3170b57cec5SDimitry Andric const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>();
3180b57cec5SDimitry Andric if (!TT) return false;
3190b57cec5SDimitry Andric
3200b57cec5SDimitry Andric return TT->getDecl()->getIdentifier() == II;
3210b57cec5SDimitry Andric }
3220b57cec5SDimitry Andric
registerNSOrCFErrorDerefChecker(CheckerManager & mgr)3230b57cec5SDimitry Andric void ento::registerNSOrCFErrorDerefChecker(CheckerManager &mgr) {
3240b57cec5SDimitry Andric mgr.registerChecker<NSOrCFErrorDerefChecker>();
3250b57cec5SDimitry Andric }
3260b57cec5SDimitry Andric
shouldRegisterNSOrCFErrorDerefChecker(const CheckerManager & mgr)3275ffd83dbSDimitry Andric bool ento::shouldRegisterNSOrCFErrorDerefChecker(const CheckerManager &mgr) {
3280b57cec5SDimitry Andric return true;
3290b57cec5SDimitry Andric }
3300b57cec5SDimitry Andric
registerNSErrorChecker(CheckerManager & mgr)3310b57cec5SDimitry Andric void ento::registerNSErrorChecker(CheckerManager &mgr) {
3320b57cec5SDimitry Andric mgr.registerChecker<NSErrorMethodChecker>();
3330b57cec5SDimitry Andric NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
3340b57cec5SDimitry Andric checker->ShouldCheckNSError = true;
3355ffd83dbSDimitry Andric checker->NSErrorName = mgr.getCurrentCheckerName();
3360b57cec5SDimitry Andric }
3370b57cec5SDimitry Andric
shouldRegisterNSErrorChecker(const CheckerManager & mgr)3385ffd83dbSDimitry Andric bool ento::shouldRegisterNSErrorChecker(const CheckerManager &mgr) {
3390b57cec5SDimitry Andric return true;
3400b57cec5SDimitry Andric }
3410b57cec5SDimitry Andric
registerCFErrorChecker(CheckerManager & mgr)3420b57cec5SDimitry Andric void ento::registerCFErrorChecker(CheckerManager &mgr) {
3430b57cec5SDimitry Andric mgr.registerChecker<CFErrorFunctionChecker>();
3440b57cec5SDimitry Andric NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
3450b57cec5SDimitry Andric checker->ShouldCheckCFError = true;
3465ffd83dbSDimitry Andric checker->CFErrorName = mgr.getCurrentCheckerName();
3470b57cec5SDimitry Andric }
3480b57cec5SDimitry Andric
shouldRegisterCFErrorChecker(const CheckerManager & mgr)3495ffd83dbSDimitry Andric bool ento::shouldRegisterCFErrorChecker(const CheckerManager &mgr) {
3500b57cec5SDimitry Andric return true;
3510b57cec5SDimitry Andric }
352