697 lines · cpp
1//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This defines CallAndMessageChecker, a builtin checker that checks for various10// errors of call and objc message expressions.11//12//===----------------------------------------------------------------------===//13 14#include "clang/AST/ExprCXX.h"15#include "clang/AST/ParentMap.h"16#include "clang/Basic/TargetInfo.h"17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"18#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"19#include "clang/StaticAnalyzer/Core/Checker.h"20#include "clang/StaticAnalyzer/Core/CheckerManager.h"21#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"22#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"23#include "llvm/ADT/StringExtras.h"24#include "llvm/Support/raw_ostream.h"25 26using namespace clang;27using namespace ento;28 29namespace {30 31class CallAndMessageChecker32 : public Checker<check::PreObjCMessage, check::ObjCMessageNil,33 check::PreCall> {34 const BugType CallNullBug{35 this, "Called function pointer is null (null dereference)"};36 const BugType CallUndefBug{37 this, "Called function pointer is an uninitialized pointer value"};38 const BugType CXXCallNullBug{this, "Called C++ object pointer is null"};39 const BugType CXXCallUndefBug{this,40 "Called C++ object pointer is uninitialized"};41 const BugType CallArgBug{this, "Uninitialized argument value"};42 const BugType CXXDeleteUndefBug{this, "Uninitialized argument value"};43 const BugType MsgUndefBug{44 this, "Receiver in message expression is an uninitialized value"};45 const BugType ObjCPropUndefBug{46 this, "Property access on an uninitialized object pointer"};47 const BugType ObjCSubscriptUndefBug{48 this, "Subscript access on an uninitialized object pointer"};49 const BugType MsgArgBug{this, "Uninitialized argument value"};50 const BugType MsgRetBug{this, "Receiver in message expression is 'nil'"};51 const BugType CallFewArgsBug{this, "Function call with too few arguments"};52 53public:54 // Like a checker family, CallAndMessageChecker can produce many kinds of55 // warnings which can be separately enabled or disabled. However, for56 // historical reasons these warning kinds are represented by checker options57 // (and not separate checker frontends with their own names) because58 // CallAndMessage is among the oldest checkers out there, and can59 // be responsible for the majority of the reports on any given project. This60 // is obviously not ideal, but changing checker name has the consequence of61 // changing the issue hashes associated with the reports, and databases62 // relying on this (CodeChecker, for instance) would suffer greatly.63 // If we ever end up making changes to the issue hash generation algorithm, or64 // the warning messages here, we should totally jump on the opportunity to65 // convert these to actual checker frontends.66 enum CheckKind {67 CK_FunctionPointer,68 CK_ParameterCount,69 CK_CXXThisMethodCall,70 CK_CXXDeallocationArg,71 CK_ArgInitializedness,72 CK_ArgPointeeInitializedness,73 CK_NilReceiver,74 CK_UndefReceiver,75 CK_NumCheckKinds76 };77 78 bool ChecksEnabled[CK_NumCheckKinds] = {false};79 80 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;81 82 /// Fill in the return value that results from messaging nil based on the83 /// return type and architecture and diagnose if the return value will be84 /// garbage.85 void checkObjCMessageNil(const ObjCMethodCall &msg, CheckerContext &C) const;86 87 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;88 89 ProgramStateRef checkFunctionPointerCall(const CallExpr *CE,90 CheckerContext &C,91 ProgramStateRef State) const;92 93 ProgramStateRef checkCXXMethodCall(const CXXInstanceCall *CC,94 CheckerContext &C,95 ProgramStateRef State) const;96 97 ProgramStateRef checkParameterCount(const CallEvent &Call, CheckerContext &C,98 ProgramStateRef State) const;99 100 ProgramStateRef checkCXXDeallocation(const CXXDeallocatorCall *DC,101 CheckerContext &C,102 ProgramStateRef State) const;103 104 ProgramStateRef checkArgInitializedness(const CallEvent &Call,105 CheckerContext &C,106 ProgramStateRef State) const;107 108private:109 bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,110 const Expr *ArgEx, int ArgumentNumber,111 bool CheckUninitFields, const CallEvent &Call,112 const BugType &BT,113 const ParmVarDecl *ParamDecl) const;114 115 static void emitBadCall(const BugType &BT, CheckerContext &C,116 const Expr *BadE);117 void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,118 ExplodedNode *N) const;119 120 void HandleNilReceiver(CheckerContext &C,121 ProgramStateRef state,122 const ObjCMethodCall &msg) const;123 124 bool uninitRefOrPointer(CheckerContext &C, SVal V, SourceRange ArgRange,125 const Expr *ArgEx, const BugType &BT,126 const ParmVarDecl *ParamDecl,127 int ArgumentNumber) const;128};129} // end anonymous namespace130 131void CallAndMessageChecker::emitBadCall(const BugType &BT, CheckerContext &C,132 const Expr *BadE) {133 ExplodedNode *N = C.generateErrorNode();134 if (!N)135 return;136 137 auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N);138 if (BadE) {139 R->addRange(BadE->getSourceRange());140 if (BadE->isGLValue())141 BadE = bugreporter::getDerefExpr(BadE);142 bugreporter::trackExpressionValue(N, BadE, *R);143 }144 C.emitReport(std::move(R));145}146 147static void describeUninitializedArgumentInCall(const CallEvent &Call,148 int ArgumentNumber,149 llvm::raw_svector_ostream &Os) {150 switch (Call.getKind()) {151 case CE_ObjCMessage: {152 const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);153 switch (Msg.getMessageKind()) {154 case OCM_Message:155 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)156 << " argument in message expression is an uninitialized value";157 return;158 case OCM_PropertyAccess:159 assert(Msg.isSetter() && "Getters have no args");160 Os << "Argument for property setter is an uninitialized value";161 return;162 case OCM_Subscript:163 if (Msg.isSetter() && (ArgumentNumber == 0))164 Os << "Argument for subscript setter is an uninitialized value";165 else166 Os << "Subscript index is an uninitialized value";167 return;168 }169 llvm_unreachable("Unknown message kind.");170 }171 case CE_Block:172 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)173 << " block call argument is an uninitialized value";174 return;175 default:176 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)177 << " function call argument is an uninitialized value";178 return;179 }180}181 182bool CallAndMessageChecker::uninitRefOrPointer(183 CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,184 const BugType &BT, const ParmVarDecl *ParamDecl, int ArgumentNumber) const {185 186 // The pointee being uninitialized is a sign of code smell, not a bug, no need187 // to sink here.188 if (!ChecksEnabled[CK_ArgPointeeInitializedness])189 return false;190 191 // No parameter declaration available, i.e. variadic function argument.192 if(!ParamDecl)193 return false;194 195 // If parameter is declared as pointer to const in function declaration,196 // then check if corresponding argument in function call is197 // pointing to undefined symbol value (uninitialized memory).198 SmallString<200> Buf;199 llvm::raw_svector_ostream Os(Buf);200 201 if (ParamDecl->getType()->isPointerType()) {202 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)203 << " function call argument is a pointer to uninitialized value";204 } else if (ParamDecl->getType()->isReferenceType()) {205 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)206 << " function call argument is an uninitialized value";207 } else208 return false;209 210 if(!ParamDecl->getType()->getPointeeType().isConstQualified())211 return false;212 213 if (const MemRegion *SValMemRegion = V.getAsRegion()) {214 const ProgramStateRef State = C.getState();215 const SVal PSV = State->getSVal(SValMemRegion, C.getASTContext().CharTy);216 if (PSV.isUndef()) {217 if (ExplodedNode *N = C.generateErrorNode()) {218 auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);219 R->addRange(ArgRange);220 if (ArgEx)221 bugreporter::trackExpressionValue(N, ArgEx, *R);222 223 C.emitReport(std::move(R));224 }225 return true;226 }227 }228 return false;229}230 231namespace {232class FindUninitializedField {233public:234 SmallVector<const FieldDecl *, 10> FieldChain;235 236private:237 StoreManager &StoreMgr;238 MemRegionManager &MrMgr;239 Store store;240 241public:242 FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr,243 Store s)244 : StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}245 246 bool Find(const TypedValueRegion *R) {247 QualType T = R->getValueType();248 if (const RecordType *RT = T->getAsStructureType()) {249 const RecordDecl *RD = RT->getDecl()->getDefinition();250 assert(RD && "Referred record has no definition");251 for (const auto *I : RD->fields()) {252 if (I->isUnnamedBitField())253 continue;254 const FieldRegion *FR = MrMgr.getFieldRegion(I, R);255 FieldChain.push_back(I);256 T = I->getType();257 if (T->isStructureType()) {258 if (Find(FR))259 return true;260 } else {261 SVal V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));262 if (V.isUndef())263 return true;264 }265 FieldChain.pop_back();266 }267 }268 269 return false;270 }271};272} // namespace273 274bool CallAndMessageChecker::PreVisitProcessArg(275 CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,276 int ArgumentNumber, bool CheckUninitFields, const CallEvent &Call,277 const BugType &BT, const ParmVarDecl *ParamDecl) const {278 if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, ArgumentNumber))279 return true;280 281 if (V.isUndef()) {282 if (!ChecksEnabled[CK_ArgInitializedness]) {283 C.addSink();284 return true;285 }286 if (ExplodedNode *N = C.generateErrorNode()) {287 // Generate a report for this bug.288 SmallString<200> Buf;289 llvm::raw_svector_ostream Os(Buf);290 describeUninitializedArgumentInCall(Call, ArgumentNumber, Os);291 auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);292 293 R->addRange(ArgRange);294 if (ArgEx)295 bugreporter::trackExpressionValue(N, ArgEx, *R);296 C.emitReport(std::move(R));297 }298 return true;299 }300 301 if (!CheckUninitFields)302 return false;303 304 if (auto LV = V.getAs<nonloc::LazyCompoundVal>()) {305 const LazyCompoundValData *D = LV->getCVData();306 FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),307 C.getSValBuilder().getRegionManager(),308 D->getStore());309 310 if (F.Find(D->getRegion())) {311 if (!ChecksEnabled[CK_ArgInitializedness]) {312 C.addSink();313 return true;314 }315 if (ExplodedNode *N = C.generateErrorNode()) {316 SmallString<512> Str;317 llvm::raw_svector_ostream os(Str);318 os << "Passed-by-value struct argument contains uninitialized data";319 320 if (F.FieldChain.size() == 1)321 os << " (e.g., field: '" << *F.FieldChain[0] << "')";322 else {323 os << " (e.g., via the field chain: '";324 bool first = true;325 for (const FieldDecl *FD : F.FieldChain) {326 if (first)327 first = false;328 else329 os << '.';330 os << *FD;331 }332 os << "')";333 }334 335 // Generate a report for this bug.336 auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);337 R->addRange(ArgRange);338 339 if (ArgEx)340 bugreporter::trackExpressionValue(N, ArgEx, *R);341 // FIXME: enhance track back for uninitialized value for arbitrary342 // memregions343 C.emitReport(std::move(R));344 }345 return true;346 }347 }348 349 return false;350}351 352ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall(353 const CallExpr *CE, CheckerContext &C, ProgramStateRef State) const {354 355 const Expr *Callee = CE->getCallee()->IgnoreParens();356 const LocationContext *LCtx = C.getLocationContext();357 SVal L = State->getSVal(Callee, LCtx);358 359 if (L.isUndef()) {360 if (!ChecksEnabled[CK_FunctionPointer]) {361 C.addSink(State);362 return nullptr;363 }364 emitBadCall(CallUndefBug, C, Callee);365 return nullptr;366 }367 368 ProgramStateRef StNonNull, StNull;369 std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());370 371 if (StNull && !StNonNull) {372 if (!ChecksEnabled[CK_FunctionPointer]) {373 C.addSink(StNull);374 return nullptr;375 }376 emitBadCall(CallNullBug, C, Callee);377 return nullptr;378 }379 380 return StNonNull;381}382 383ProgramStateRef CallAndMessageChecker::checkParameterCount(384 const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {385 386 // If we have a function or block declaration, we can make sure we pass387 // enough parameters.388 unsigned Params = Call.parameters().size();389 if (Call.getNumArgs() >= Params)390 return State;391 392 if (!ChecksEnabled[CK_ParameterCount]) {393 C.addSink(State);394 return nullptr;395 }396 397 ExplodedNode *N = C.generateErrorNode();398 if (!N)399 return nullptr;400 401 SmallString<512> Str;402 llvm::raw_svector_ostream os(Str);403 if (isa<AnyFunctionCall>(Call)) {404 os << "Function ";405 } else {406 assert(isa<BlockCall>(Call));407 os << "Block ";408 }409 os << "taking " << Params << " argument" << (Params == 1 ? "" : "s")410 << " is called with fewer (" << Call.getNumArgs() << ")";411 412 C.emitReport(413 std::make_unique<PathSensitiveBugReport>(CallFewArgsBug, os.str(), N));414 return nullptr;415}416 417ProgramStateRef CallAndMessageChecker::checkCXXMethodCall(418 const CXXInstanceCall *CC, CheckerContext &C, ProgramStateRef State) const {419 420 SVal V = CC->getCXXThisVal();421 if (V.isUndef()) {422 if (!ChecksEnabled[CK_CXXThisMethodCall]) {423 C.addSink(State);424 return nullptr;425 }426 emitBadCall(CXXCallUndefBug, C, CC->getCXXThisExpr());427 return nullptr;428 }429 430 ProgramStateRef StNonNull, StNull;431 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());432 433 if (StNull && !StNonNull) {434 if (!ChecksEnabled[CK_CXXThisMethodCall]) {435 C.addSink(StNull);436 return nullptr;437 }438 emitBadCall(CXXCallNullBug, C, CC->getCXXThisExpr());439 return nullptr;440 }441 442 return StNonNull;443}444 445ProgramStateRef446CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC,447 CheckerContext &C,448 ProgramStateRef State) const {449 const CXXDeleteExpr *DE = DC->getOriginExpr();450 assert(DE);451 SVal Arg = C.getSVal(DE->getArgument());452 if (!Arg.isUndef())453 return State;454 455 if (!ChecksEnabled[CK_CXXDeallocationArg]) {456 C.addSink(State);457 return nullptr;458 }459 460 StringRef Desc;461 ExplodedNode *N = C.generateErrorNode();462 if (!N)463 return nullptr;464 if (DE->isArrayFormAsWritten())465 Desc = "Argument to 'delete[]' is uninitialized";466 else467 Desc = "Argument to 'delete' is uninitialized";468 auto R = std::make_unique<PathSensitiveBugReport>(CXXDeleteUndefBug, Desc, N);469 bugreporter::trackExpressionValue(N, DE, *R);470 C.emitReport(std::move(R));471 return nullptr;472}473 474ProgramStateRef CallAndMessageChecker::checkArgInitializedness(475 const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {476 477 const Decl *D = Call.getDecl();478 479 // Don't check for uninitialized field values in arguments if the480 // caller has a body that is available and we have the chance to inline it.481 // This is a hack, but is a reasonable compromise betweens sometimes warning482 // and sometimes not depending on if we decide to inline a function.483 const bool checkUninitFields =484 !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));485 486 const BugType &BT = isa<ObjCMethodCall>(Call) ? MsgArgBug : CallArgBug;487 488 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);489 for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {490 const ParmVarDecl *ParamDecl = nullptr;491 if (FD && i < FD->getNumParams())492 ParamDecl = FD->getParamDecl(i);493 if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),494 Call.getArgExpr(i), i, checkUninitFields, Call, BT,495 ParamDecl))496 return nullptr;497 }498 return State;499}500 501void CallAndMessageChecker::checkPreCall(const CallEvent &Call,502 CheckerContext &C) const {503 ProgramStateRef State = C.getState();504 505 if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()))506 State = checkFunctionPointerCall(CE, C, State);507 508 if (!State)509 return;510 511 if (Call.getDecl())512 State = checkParameterCount(Call, C, State);513 514 if (!State)515 return;516 517 if (const auto *CC = dyn_cast<CXXInstanceCall>(&Call))518 State = checkCXXMethodCall(CC, C, State);519 520 if (!State)521 return;522 523 if (const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call))524 State = checkCXXDeallocation(DC, C, State);525 526 if (!State)527 return;528 529 State = checkArgInitializedness(Call, C, State);530 531 // If we make it here, record our assumptions about the callee.532 C.addTransition(State);533}534 535void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,536 CheckerContext &C) const {537 SVal recVal = msg.getReceiverSVal();538 if (recVal.isUndef()) {539 if (!ChecksEnabled[CK_UndefReceiver]) {540 C.addSink();541 return;542 }543 if (ExplodedNode *N = C.generateErrorNode()) {544 const BugType *BT = nullptr;545 switch (msg.getMessageKind()) {546 case OCM_Message:547 BT = &MsgUndefBug;548 break;549 case OCM_PropertyAccess:550 BT = &ObjCPropUndefBug;551 break;552 case OCM_Subscript:553 BT = &ObjCSubscriptUndefBug;554 break;555 }556 assert(BT && "Unknown message kind.");557 558 auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);559 const ObjCMessageExpr *ME = msg.getOriginExpr();560 R->addRange(ME->getReceiverRange());561 562 // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.563 if (const Expr *ReceiverE = ME->getInstanceReceiver())564 bugreporter::trackExpressionValue(N, ReceiverE, *R);565 C.emitReport(std::move(R));566 }567 return;568 }569}570 571void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,572 CheckerContext &C) const {573 HandleNilReceiver(C, C.getState(), msg);574}575 576void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,577 const ObjCMethodCall &msg,578 ExplodedNode *N) const {579 if (!ChecksEnabled[CK_NilReceiver]) {580 C.addSink();581 return;582 }583 584 const ObjCMessageExpr *ME = msg.getOriginExpr();585 586 QualType ResTy = msg.getResultType();587 588 SmallString<200> buf;589 llvm::raw_svector_ostream os(buf);590 os << "The receiver of message '";591 ME->getSelector().print(os);592 os << "' is nil";593 if (ResTy->isReferenceType()) {594 os << ", which results in forming a null reference";595 } else {596 os << " and returns a value of type '";597 msg.getResultType().print(os, C.getLangOpts());598 os << "' that will be garbage";599 }600 601 auto report =602 std::make_unique<PathSensitiveBugReport>(MsgRetBug, os.str(), N);603 report->addRange(ME->getReceiverRange());604 // FIXME: This won't track "self" in messages to super.605 if (const Expr *receiver = ME->getInstanceReceiver()) {606 bugreporter::trackExpressionValue(N, receiver, *report);607 }608 C.emitReport(std::move(report));609}610 611static bool supportsNilWithFloatRet(const llvm::Triple &triple) {612 return (triple.getVendor() == llvm::Triple::Apple &&613 (triple.isiOS() || triple.isWatchOS() ||614 !triple.isMacOSXVersionLT(10,5)));615}616 617void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,618 ProgramStateRef state,619 const ObjCMethodCall &Msg) const {620 ASTContext &Ctx = C.getASTContext();621 622 // Check the return type of the message expression. A message to nil will623 // return different values depending on the return type and the architecture.624 QualType RetTy = Msg.getResultType();625 CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);626 const LocationContext *LCtx = C.getLocationContext();627 628 if (CanRetTy->isStructureOrClassType()) {629 // Structure returns are safe since the compiler zeroes them out.630 SVal V = C.getSValBuilder().makeZeroVal(RetTy);631 C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V));632 return;633 }634 635 // Other cases: check if sizeof(return type) > sizeof(void*)636 if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap()637 .isConsumedExpr(Msg.getOriginExpr())) {638 // Compute: sizeof(void *) and sizeof(return type)639 const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);640 const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);641 642 if (CanRetTy.getTypePtr()->isReferenceType()||643 (voidPtrSize < returnTypeSize &&644 !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) &&645 (Ctx.FloatTy == CanRetTy ||646 Ctx.DoubleTy == CanRetTy ||647 Ctx.LongDoubleTy == CanRetTy ||648 Ctx.LongLongTy == CanRetTy ||649 Ctx.UnsignedLongLongTy == CanRetTy)))) {650 if (ExplodedNode *N = C.generateErrorNode(state))651 emitNilReceiverBug(C, Msg, N);652 return;653 }654 655 // Handle the safe cases where the return value is 0 if the656 // receiver is nil.657 //658 // FIXME: For now take the conservative approach that we only659 // return null values if we *know* that the receiver is nil.660 // This is because we can have surprises like:661 //662 // ... = [[NSScreens screens] objectAtIndex:0];663 //664 // What can happen is that [... screens] could return nil, but665 // it most likely isn't nil. We should assume the semantics666 // of this case unless we have *a lot* more knowledge.667 //668 SVal V = C.getSValBuilder().makeZeroVal(RetTy);669 C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V));670 return;671 }672 673 C.addTransition(state);674}675 676void ento::registerCallAndMessageChecker(CheckerManager &Mgr) {677 CallAndMessageChecker *Chk = Mgr.registerChecker<CallAndMessageChecker>();678 679#define QUERY_CHECKER_OPTION(OPTION) \680 Chk->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \681 Mgr.getAnalyzerOptions().getCheckerBooleanOption( \682 Mgr.getCurrentCheckerName(), #OPTION);683 684 QUERY_CHECKER_OPTION(FunctionPointer)685 QUERY_CHECKER_OPTION(ParameterCount)686 QUERY_CHECKER_OPTION(CXXThisMethodCall)687 QUERY_CHECKER_OPTION(CXXDeallocationArg)688 QUERY_CHECKER_OPTION(ArgInitializedness)689 QUERY_CHECKER_OPTION(ArgPointeeInitializedness)690 QUERY_CHECKER_OPTION(NilReceiver)691 QUERY_CHECKER_OPTION(UndefReceiver)692}693 694bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &) {695 return true;696}697