1//=======- RawPtrRefCallArgsChecker.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-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "ASTUtils.h"
10#include "DiagOutputUtils.h"
11#include "PtrTypesSemantics.h"
12#include "RawPtrRefSafetyModel.h"
13#include "clang/AST/Decl.h"
14#include "clang/AST/DeclCXX.h"
15#include "clang/AST/DynamicRecursiveASTVisitor.h"
16#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
17#include "clang/Basic/SourceLocation.h"
18#include "clang/Lex/Lexer.h"
19#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
20#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22#include "clang/StaticAnalyzer/Core/Checker.h"
23#include "llvm/Support/SaveAndRestore.h"
24#include <optional>
25
26using namespace clang;
27using namespace ento;
28
29namespace {
30
31class RawPtrRefCallArgsChecker
32 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
33 BugType Bug;
34
35 TrivialFunctionAnalysis TFA;
36 EnsureFunctionAnalysis EFA;
37
38protected:
39 mutable BugReporter *BR;
40 const std::unique_ptr<PtrRefSafetyModel> Model;
41
42public:
43 RawPtrRefCallArgsChecker(const char *description,
44 std::unique_ptr<PtrRefSafetyModel> Model)
45 : Bug(this, description, "WebKit coding guidelines"),
46 Model(std::move(Model)) {}
47
48 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
49 BugReporter &BRArg) const {
50 BR = &BRArg;
51
52 // The calls to checkAST* from AnalysisConsumer don't
53 // visit template instantiations or lambda classes. We
54 // want to visit those, so we make our own RecursiveASTVisitor.
55 struct LocalVisitor : DynamicRecursiveASTVisitor {
56 const RawPtrRefCallArgsChecker *Checker;
57 Decl *DeclWithIssue{nullptr};
58
59 explicit LocalVisitor(const RawPtrRefCallArgsChecker *Checker)
60 : Checker(Checker) {
61 assert(Checker);
62 ShouldVisitTemplateInstantiations = true;
63 ShouldVisitImplicitCode = false;
64 }
65
66 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {
67 if (isSmartPtrClass(Name: safeGetName(ASTNode: Decl)))
68 return true;
69 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(D: Decl);
70 }
71
72 bool TraverseDecl(Decl *D) override {
73 // A template pattern is checked through its instantiations, which are
74 // traversed from the TemplateDecl itself. In the pattern the callee of
75 // a call may still be an unresolved overload set and the type of an
76 // expression may still be dependent, neither of which can be reasoned
77 // about, so don't enter it at all.
78 if (D && !isa<TemplateDecl>(Val: D) && D->isTemplated())
79 return true;
80 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
81 if (D && (isa<FunctionDecl>(Val: D) || isa<ObjCMethodDecl>(Val: D)))
82 DeclWithIssue = D;
83 return DynamicRecursiveASTVisitor::TraverseDecl(D);
84 }
85
86 bool TraverseLambdaExpr(LambdaExpr *L) override {
87 auto *FTD = L->getLambdaClass()->getDependentLambdaCallOperator();
88 if (!FTD)
89 return DynamicRecursiveASTVisitor::TraverseLambdaExpr(S: L);
90 // The body of a generic lambda is the pattern of its call operator,
91 // but it is reached from the LambdaExpr as a statement, so TraverseDecl
92 // never gets to skip it. Traverse the capture initializers, which are
93 // evaluated in the enclosing scope, and then the call operator itself,
94 // of which the pattern is skipped like any other and the instantiations
95 // are traversed. The initializers are traversed as expressions because
96 // the variable of an init capture is declared in the pattern.
97 for (unsigned I = 0, N = L->capture_size(); I != N; ++I) {
98 if (!(L->capture_begin() + I)->isExplicit())
99 continue;
100 if (auto *Init = L->capture_init_begin()[I];
101 Init && !TraverseStmt(S: Init))
102 return false;
103 }
104 return TraverseDecl(D: FTD);
105 }
106
107 bool VisitCallExpr(CallExpr *CE) override {
108 Checker->visitCallExpr(CE, D: DeclWithIssue);
109 return true;
110 }
111
112 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
113 Checker->visitConstructExpr(CE, D: DeclWithIssue);
114 return true;
115 }
116
117 bool VisitTypedefDecl(TypedefDecl *TD) override {
118 if (auto *RTC = Checker->Model->retainTypeChecker())
119 RTC->visitTypedef(TD);
120 return true;
121 }
122
123 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr) override {
124 Checker->visitObjCMessageExpr(E: ObjCMsgExpr, D: DeclWithIssue);
125 return true;
126 }
127 };
128
129 LocalVisitor visitor(this);
130 if (auto *RTC = Model->retainTypeChecker())
131 RTC->visitTranslationUnitDecl(TUD);
132 visitor.TraverseDecl(D: const_cast<TranslationUnitDecl *>(TUD));
133 }
134
135 template <typename CallOrConstruct>
136 void visitCallOrConstructExpr(const CallOrConstruct *CE,
137 const FunctionDecl *F, const Decl *D) const {
138 if (F) {
139 unsigned ArgIdx = 0;
140 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
141 checkThisArg(F, MemberCallExpr, D);
142 } else if (isa<CXXOperatorCallExpr>(CE) && isa<CXXMethodDecl>(Val: F)) {
143 // An overloaded member operator (e.g. lambda or std::function call
144 // operator) receives the receiver object as argument 0; start the
145 // parameter loop at 1 so we don't match it against the operator's
146 // first declared parameter.
147 auto *Receiver = CE->getArg(0);
148 checkThisArg(F, Receiver, Receiver->getType(), D);
149 ArgIdx = 1;
150 }
151
152 for (auto P = F->param_begin();
153 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
154 // TODO: attributes.
155 // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
156 // continue;
157 checkArg(Callee: F, Arg: CE->getArg(ArgIdx), ParamType: (*P)->getType(), Param: *P, DeclWithIssue: D);
158 }
159 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
160 auto *Arg = CE->getArg(ArgIdx);
161 checkArg(Callee: F, Arg, ParamType: Arg->getType(), Param: nullptr, DeclWithIssue: D);
162 }
163 }
164 }
165
166 void visitCallExpr(const CallExpr *CE, const Decl *D) const {
167 auto *Callee = CE->getDirectCallee();
168 if (shouldSkipCall(CE, Callee))
169 return;
170
171 if (Callee)
172 visitCallOrConstructExpr(CE, F: Callee, D);
173 else if (auto *Decl = CE->getCalleeDecl()) {
174 if (auto *FnType = Decl->getFunctionType()) {
175 if (auto *ProtoType = dyn_cast<FunctionProtoType>(Val: FnType)) {
176 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(Val: CE))
177 checkThisArg(Callee: nullptr, MemberCallExpr, DeclWithIssue: D);
178 unsigned ArgIdx = 0;
179 for (auto PT = ProtoType->param_type_begin();
180 PT < ProtoType->param_type_end() && ArgIdx < CE->getNumArgs();
181 ++PT, ++ArgIdx)
182 checkArg(Callee: nullptr, Arg: CE->getArg(Arg: ArgIdx), ParamType: *PT, Param: nullptr, DeclWithIssue: D);
183 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
184 auto *Arg = CE->getArg(Arg: ArgIdx);
185 checkArg(Callee: nullptr, Arg, ParamType: Arg->getType(), Param: nullptr, DeclWithIssue: D);
186 }
187 }
188 }
189 }
190 }
191
192 void visitConstructExpr(const CXXConstructExpr *CE, const Decl *D) const {
193 auto *Constructor = CE->getConstructor();
194 if (shouldSkipCall(CE, Callee: Constructor))
195 return;
196 if (Constructor)
197 visitCallOrConstructExpr(CE, F: Constructor, D);
198 }
199
200 void visitObjCMessageExpr(const ObjCMessageExpr *E, const Decl *D) const {
201 if (BR->getSourceManager().isInSystemHeader(Loc: E->getExprLoc()))
202 return;
203
204 if (auto *Receiver = E->getInstanceReceiver()) {
205 std::optional<bool> IsUnsafe = Model->isUnsafePtr(QT: E->getReceiverType());
206 const Expr *Origin = nullptr;
207 if (IsUnsafe && *IsUnsafe &&
208 !isPtrOriginSafe(Arg: Receiver, Origin: &Origin, SinkType: E->getReceiverType())) {
209 if (isAllocInit(E))
210 return;
211 reportBugOnReceiver(Callee: E->getMethodDecl(), CallArg: Receiver, DeclWithIssue: D, Origin);
212 }
213 }
214
215 auto *MethodDecl = E->getMethodDecl();
216 if (!MethodDecl)
217 return;
218
219 auto ArgCount = E->getNumArgs();
220 for (unsigned i = 0; i < ArgCount; ++i) {
221 auto *Arg = E->getArg(Arg: i);
222 bool hasParam = i < MethodDecl->param_size();
223 auto *Param = hasParam ? MethodDecl->getParamDecl(Idx: i) : nullptr;
224 auto ArgType = Arg->getType();
225 std::optional<bool> IsUnsafe = Model->isUnsafePtr(QT: ArgType);
226 if (!IsUnsafe || !(*IsUnsafe))
227 continue;
228 const Expr *Origin = nullptr;
229 if (isPtrOriginSafe(Arg, Origin: &Origin, SinkType: ArgType))
230 continue;
231 reportBug(Callee: MethodDecl, CallArg: Arg, Param, DeclWithIssue: D, Origin);
232 }
233 }
234
235 static bool isRefCountingOperation(const CXXMethodDecl *MD) {
236 if (!MD)
237 return false;
238 auto name = safeGetName(ASTNode: MD);
239 return name == "ref" || name == "deref" ||
240 name == "incrementCheckedPtrCount" ||
241 name == "decrementCheckedPtrCount";
242 }
243
244 void checkThisArg(const NamedDecl *Callee,
245 const CXXMemberCallExpr *MemberCallExpr,
246 const Decl *DeclWithIssue) const {
247 if (isRefCountingOperation(MD: MemberCallExpr->getMethodDecl()))
248 return;
249 checkThisArg(Callee, Receiver: MemberCallExpr->getImplicitObjectArgument(),
250 ReceiverType: MemberCallExpr->getObjectType(), DeclWithIssue);
251 }
252
253 void checkThisArg(const NamedDecl *Callee, const Expr *Receiver,
254 QualType ReceiverType, const Decl *DeclWithIssue) const {
255 // There is no ParmVarDecl for the implicit object parameter, so
256 // synthesize its type ('T&' per [over.match.funcs]) for the model to
257 // classify.
258 QualType ParamType = BR->getContext().getLValueReferenceType(
259 T: ReceiverType.getCanonicalType());
260 std::optional<bool> IsUnsafe = Model->isUnsafePtr(QT: ParamType);
261 if (!IsUnsafe || !*IsUnsafe)
262 return;
263
264 const Expr *Origin = nullptr;
265 if (isPtrOriginSafe(Arg: Receiver, Origin: &Origin, SinkType: ParamType))
266 return;
267
268 reportBugOnThis(Callee, CallArg: Receiver, DeclWithIssue, Origin);
269 }
270
271 void checkArg(const NamedDecl *Callee, const Expr *Arg, QualType ParamType,
272 const ParmVarDecl *Param, const Decl *DeclWithIssue) const {
273 std::optional<bool> IsUncounted = Model->isUnsafePtr(QT: ParamType);
274 if (!IsUncounted || !(*IsUncounted))
275 return;
276
277 if (auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(Val: Arg))
278 Arg = DefaultArg->getExpr();
279
280 const Expr *Origin = nullptr;
281 if (isPtrOriginSafe(Arg, Origin: &Origin, SinkType: ParamType))
282 return;
283
284 reportBug(Callee, CallArg: Arg, Param, DeclWithIssue, Origin);
285 }
286
287 bool isPtrOriginSafe(const Expr *Arg, const Expr **Origin = nullptr,
288 QualType SinkType = QualType()) const {
289 return tryToFindPtrOrigin(
290 E: Arg, /*StopAtFirstRefCountedObj=*/true,
291 FollowLifetimeBound: Model->checksForInteriorDestruction(),
292 isSafePtr: [&](const clang::CXXRecordDecl *Record) {
293 return Model->isSafePtr(Record);
294 },
295 isSafePtrType: [&](const clang::QualType T) { return Model->isSafePtrType(T); },
296 isSafeGlobalDecl: [&](const clang::Decl *D) {
297 return Model->isSafeDecl(D, BR->getSourceManager());
298 },
299 // A temporary on the path to an argument's origin is safe: the full
300 // expression does not end until the call returns.
301 callback: [&](const clang::Expr *ArgOrigin, bool IsSafe,
302 bool /*OriginDependsOnFullExpressionTemporary*/,
303 bool PtrIsLifetimeBoundToOrigin) {
304 if (IsSafe)
305 return true;
306 if (isNullPtr(E: ArgOrigin))
307 return true;
308 if (isa<IntegerLiteral>(Val: ArgOrigin)) {
309 // FIXME: Check the value.
310 // foo(123)
311 return true;
312 }
313 if (isa<CXXBoolLiteralExpr>(Val: ArgOrigin))
314 return true;
315 if (isa<ObjCStringLiteral>(Val: ArgOrigin))
316 return true;
317 if (!Model->checksForInteriorDestruction() &&
318 originOutlivesCall(E: ArgOrigin))
319 return true;
320 if (EFA.isACallToEnsureFn(E: ArgOrigin)) {
321 auto *MCE = dyn_cast<CXXMemberCallExpr>(Val: ArgOrigin);
322 assert(MCE);
323 if (isPtrOriginSafe(Arg: MCE->getImplicitObjectArgument()))
324 return true;
325 }
326 if (Model->isSafeExpr(Origin: ArgOrigin, PtrIsLifetimeBoundToOrigin, SinkType,
327 /*SinkMayEscape=*/false))
328 return true;
329 if (Origin && !*Origin)
330 *Origin = ArgOrigin;
331 return false;
332 });
333 }
334
335 template <typename CallOrConstruct>
336 bool shouldSkipCall(const CallOrConstruct *CE,
337 const FunctionDecl *Callee) const {
338 if (BR->getSourceManager().isInSystemHeader(Loc: CE->getExprLoc()))
339 return true;
340
341 if (Callee && TFA.isTrivial(D: Callee))
342 return true;
343
344 if (isTrivialBuiltinFunction(F: Callee))
345 return true;
346
347 if (CE->getNumArgs() == 0)
348 return false;
349
350 // If an assignment is problematic we should warn about the sole existence
351 // of object on LHS.
352 if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
353 // Note: assignemnt to built-in type isn't derived from CallExpr.
354 if (MemberOp->getOperator() ==
355 OO_Equal) { // Ignore assignment to Ref/RefPtr.
356 auto *callee = MemberOp->getDirectCallee();
357 if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
358 if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
359 if (Model->isSafePtr(Record: classDecl))
360 return true;
361 }
362 }
363 }
364 if (MemberOp->isAssignmentOp())
365 return false;
366 }
367
368 if (!Callee)
369 return false;
370
371 if (isMethodOnWTFContainerType(Decl: Callee))
372 return true;
373
374 auto overloadedOperatorType = Callee->getOverloadedOperator();
375 if (overloadedOperatorType == OO_EqualEqual ||
376 overloadedOperatorType == OO_ExclaimEqual ||
377 overloadedOperatorType == OO_LessEqual ||
378 overloadedOperatorType == OO_GreaterEqual ||
379 overloadedOperatorType == OO_Spaceship ||
380 overloadedOperatorType == OO_AmpAmp ||
381 overloadedOperatorType == OO_PipePipe)
382 return true;
383
384 if (isCtorOfSafePtr(F: Callee) || isPtrConversion(F: Callee))
385 return true;
386
387 auto name = safeGetName(ASTNode: Callee);
388 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
389 name == "is" || name == "equal" || name == "hash" || name == "isType" ||
390 // FIXME: Most/all of these should be implemented via attributes.
391 name == "CFEqual" || name == "equalIgnoringASCIICase" ||
392 name == "equalIgnoringASCIICaseCommon" ||
393 name == "equalIgnoringNullity" || name == "toString")
394 return true;
395
396 return false;
397 }
398
399 bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
400 if (!isa<CXXMethodDecl>(Val: Decl))
401 return false;
402 auto *ClassDecl = Decl->getParent();
403 if (!ClassDecl || !isa<CXXRecordDecl>(Val: ClassDecl))
404 return false;
405
406 auto *NsDecl = ClassDecl->getParent();
407 if (!NsDecl || !isa<NamespaceDecl>(Val: NsDecl))
408 return false;
409
410 auto MethodName = safeGetName(ASTNode: Decl);
411 auto ClsNameStr = safeGetName(ASTNode: ClassDecl);
412 StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
413 auto NamespaceName = safeGetName(ASTNode: NsDecl);
414 // FIXME: These should be implemented via attributes.
415 return NamespaceName == "WTF" &&
416 (MethodName == "find" || MethodName == "findIf" ||
417 MethodName == "reverseFind" || MethodName == "reverseFindIf" ||
418 MethodName == "findIgnoringASCIICase" || MethodName == "get" ||
419 MethodName == "inlineGet" || MethodName == "contains" ||
420 MethodName == "containsIf" ||
421 MethodName == "containsIgnoringASCIICase" ||
422 MethodName == "startsWith" || MethodName == "endsWith" ||
423 MethodName == "startsWithIgnoringASCIICase" ||
424 MethodName == "endsWithIgnoringASCIICase" ||
425 MethodName == "substring") &&
426 (ClsName.ends_with(Suffix: "Vector") || ClsName.ends_with(Suffix: "Set") ||
427 ClsName.ends_with(Suffix: "Map") || ClsName == "StringImpl" ||
428 ClsName.ends_with(Suffix: "String"));
429 }
430
431 void reportBug(const NamedDecl *Callee, const Expr *CallArg,
432 const ParmVarDecl *Param, const Decl *DeclWithIssue,
433 const Expr *Origin) const {
434 assert(CallArg);
435
436 SmallString<100> Buf;
437 llvm::raw_svector_ostream Os(Buf);
438
439 const std::string paramName = safeGetName(ASTNode: Param);
440 Os << "Function argument";
441 printArgument(Os, Arg: CallArg);
442 if (!paramName.empty() || Callee)
443 Os << " (";
444 if (!paramName.empty()) {
445 Os << "parameter ";
446 printQuotedQualifiedName(Os, D: Param);
447 }
448 if (Callee) {
449 if (!paramName.empty())
450 Os << " ";
451 Os << "to ";
452 printQuotedQualifiedName(Os, D: Callee);
453 }
454 if (!paramName.empty() || Callee)
455 Os << ")";
456 Os << " is a ";
457 Model->describeHazard(Os, Origin, SinkType: CallArg->getType());
458
459 bool usesDefaultArgValue = isa<CXXDefaultArgExpr>(Val: CallArg) && Param;
460 const SourceLocation SrcLocToReport =
461 usesDefaultArgValue ? Param->getDefaultArg()->getExprLoc()
462 : CallArg->getSourceRange().getBegin();
463
464 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
465 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
466 Report->addRange(R: CallArg->getSourceRange());
467 Report->setDeclWithIssue(DeclWithIssue);
468 BR->emitReport(R: std::move(Report));
469 }
470
471 void reportBugOnThis(const NamedDecl *Callee, const Expr *CallArg,
472 const Decl *DeclWithIssue, const Expr *Origin) const {
473 assert(CallArg);
474
475 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
476
477 SmallString<100> Buf;
478 llvm::raw_svector_ostream Os(Buf);
479 Os << "Function argument";
480 printArgument(Os, Arg: CallArg);
481 Os << " (parameter 'this'";
482 if (Callee) {
483 Os << " to ";
484 printQuotedQualifiedName(Os, D: Callee);
485 }
486 Os << ") is a ";
487 printHazardOrPointerTo(Os, CallArg, Origin);
488
489 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
490 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
491 Report->addRange(R: CallArg->getSourceRange());
492 Report->setDeclWithIssue(DeclWithIssue);
493 BR->emitReport(R: std::move(Report));
494 }
495
496 void reportBugOnReceiver(const NamedDecl *Callee, const Expr *CallArg,
497 const Decl *DeclWithIssue,
498 const Expr *Origin) const {
499 assert(CallArg);
500
501 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
502
503 SmallString<100> Buf;
504 llvm::raw_svector_ostream Os(Buf);
505 Os << "Receiver";
506 printArgument(Os, Arg: CallArg);
507 if (Callee) {
508 Os << " (to ";
509 printQuotedQualifiedName(Os, D: Callee);
510 Os << ")";
511 }
512 Os << " is a ";
513 printHazardOrPointerTo(Os, CallArg, Origin);
514
515 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
516 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
517 Report->addRange(R: CallArg->getSourceRange());
518 Report->setDeclWithIssue(DeclWithIssue);
519 BR->emitReport(R: std::move(Report));
520 }
521
522 void printHazardOrPointerTo(llvm::raw_svector_ostream &Os,
523 const Expr *CallArg, const Expr *Origin) const {
524 if (Model->checksForInteriorDestruction()) {
525 Model->describeHazard(Os, Origin, SinkType: CallArg->getType());
526 return;
527 }
528 // 'this' is a pointer even when the call is spelled with '.', so don't
529 // infer pointer vs reference from the argument type.
530 Os << "raw pointer to " << Model->typeName() << " ";
531 printTypeName(Os, QT: CallArg->getType());
532 }
533
534 void printArgument(llvm::raw_svector_ostream &Os, const Expr *Arg) const {
535 SmallString<100> Buf;
536 llvm::raw_svector_ostream ArgOs(Buf);
537 Arg->printPretty(OS&: ArgOs, /*Helper=*/nullptr,
538 Policy: BR->getContext().getPrintingPolicy());
539 StringRef ArgCode = ArgOs.str();
540 if (ArgCode.contains(C: '\n'))
541 return;
542 ArgCode = ArgCode.take_front(N: 50);
543 if (ArgCode.size() == 50)
544 Os << " '" << ArgCode << "...'";
545 else
546 Os << " '" << ArgCode << "'";
547 }
548};
549
550class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
551public:
552 UncountedCallArgsChecker()
553 : RawPtrRefCallArgsChecker("Uncounted call argument for a raw "
554 "pointer/reference parameter",
555 makeRefPtrSafetyModel()) {}
556};
557
558class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker {
559public:
560 UncheckedCallArgsChecker()
561 : RawPtrRefCallArgsChecker("Unchecked call argument for a raw "
562 "pointer/reference parameter",
563 makeCheckedPtrSafetyModel()) {}
564};
565
566class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {
567public:
568 UnretainedCallArgsChecker()
569 : RawPtrRefCallArgsChecker("Unretained call argument for a raw "
570 "pointer/reference parameter",
571 makeRetainPtrSafetyModel()) {}
572};
573
574class UnborrowedCallArgsChecker final : public RawPtrRefCallArgsChecker {
575public:
576 UnborrowedCallArgsChecker()
577 : RawPtrRefCallArgsChecker("Loan on a CanBorrow object not guarded by "
578 "const or a Borrow",
579 makeBorrowSafetyModel()) {}
580};
581
582} // namespace
583
584void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
585 Mgr.registerChecker<UncountedCallArgsChecker>();
586}
587
588bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
589 return true;
590}
591
592void ento::registerUncheckedCallArgsChecker(CheckerManager &Mgr) {
593 Mgr.registerChecker<UncheckedCallArgsChecker>();
594}
595
596bool ento::shouldRegisterUncheckedCallArgsChecker(const CheckerManager &) {
597 return true;
598}
599
600void ento::registerUnretainedCallArgsChecker(CheckerManager &Mgr) {
601 Mgr.registerChecker<UnretainedCallArgsChecker>();
602}
603
604bool ento::shouldRegisterUnretainedCallArgsChecker(const CheckerManager &) {
605 return true;
606}
607
608void ento::registerUnborrowedCallArgsChecker(CheckerManager &Mgr) {
609 Mgr.registerChecker<UnborrowedCallArgsChecker>();
610}
611
612bool ento::shouldRegisterUnborrowedCallArgsChecker(const CheckerManager &) {
613 return true;
614}
615