23class RawPtrRefLambdaCapturesChecker
24 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
27 mutable BugReporter *BR =
nullptr;
28 TrivialFunctionAnalysis TFA;
31 mutable std::optional<RetainTypeChecker> RTC;
34 RawPtrRefLambdaCapturesChecker(
const char *description)
35 : Bug(this, description,
"WebKit coding guidelines") {}
37 virtual std::optional<bool>
isUnsafePtr(QualType)
const = 0;
38 virtual bool isPtrType(
const std::string &)
const = 0;
39 virtual const char *ptrKind(QualType QT)
const = 0;
41 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
42 BugReporter &BRArg)
const {
49 const RawPtrRefLambdaCapturesChecker *Checker;
50 llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
51 llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
52 llvm::DenseSet<const ValueDecl *> ProtectedThisDecls;
53 llvm::DenseSet<const CallExpr *> CallToIgnore;
54 llvm::DenseSet<const CXXConstructExpr *> ConstructToIgnore;
55 llvm::DenseMap<const VarDecl *, const LambdaExpr *> LambdaOwnerMap;
59 explicit LocalVisitor(
const RawPtrRefLambdaCapturesChecker *Checker)
62 ShouldVisitTemplateInstantiations =
true;
63 ShouldVisitImplicitCode =
false;
66 bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD)
override {
67 llvm::SaveAndRestore SavedDecl(ClsType);
70 return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD);
73 bool TraverseObjCMethodDecl(ObjCMethodDecl *OCMD)
override {
74 llvm::SaveAndRestore SavedDecl(ClsType);
77 ClsType = ImplParamDecl->getType();
79 return DynamicRecursiveASTVisitor::TraverseObjCMethodDecl(OCMD);
82 bool VisitTypedefDecl(TypedefDecl *TD)
override {
84 Checker->RTC->visitTypedef(TD);
88 bool shouldCheckThis() {
90 !ClsType.
isNull() ? Checker->isUnsafePtr(ClsType) : std::nullopt;
91 return result && *result;
95 if (LambdasToIgnore.contains(L))
97 Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
102 bool VisitVarDecl(VarDecl *VD)
override {
106 if (
auto *L = dyn_cast_or_null<LambdaExpr>(
Init->IgnoreParenCasts())) {
107 LambdasToIgnore.insert(L);
112 if (
auto *E = dyn_cast<ExprWithCleanups>(
Init))
113 Init = E->getSubExpr();
114 if (
auto *E = dyn_cast<CXXBindTemporaryExpr>(
Init))
115 Init = E->getSubExpr();
116 if (
auto *CE = dyn_cast<CallExpr>(
Init)) {
117 if (
auto *Callee = CE->getDirectCallee()) {
119 unsigned ArgCnt = CE->getNumArgs();
120 if (FnName ==
"makeScopeExit" && ArgCnt == 1) {
121 auto *Arg = CE->getArg(0);
122 if (
auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))
123 Arg = E->getSubExpr();
124 if (
auto *L = dyn_cast<LambdaExpr>(Arg)) {
125 LambdaOwnerMap.insert(std::make_pair(VD, L));
126 CallToIgnore.insert(CE);
127 LambdasToIgnore.insert(L);
129 }
else if (FnName ==
"makeVisitor") {
130 for (
unsigned ArgIndex = 0; ArgIndex < ArgCnt; ++ArgIndex) {
131 auto *Arg = CE->getArg(ArgIndex);
132 if (
auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))
133 Arg = E->getSubExpr();
134 if (
auto *L = dyn_cast<LambdaExpr>(Arg)) {
135 LambdaOwnerMap.insert(std::make_pair(VD, L));
136 CallToIgnore.insert(CE);
137 LambdasToIgnore.insert(L);
142 }
else if (
auto *CE = dyn_cast<CXXConstructExpr>(
Init)) {
143 if (
auto *Ctor = CE->getConstructor()) {
144 if (
auto *Cls = Ctor->getParent()) {
146 unsigned ArgCnt = CE->getNumArgs();
147 if (FnName ==
"ScopeExit" && ArgCnt == 1) {
148 auto *Arg = CE->getArg(0);
149 if (
auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))
150 Arg = E->getSubExpr();
151 if (
auto *L = dyn_cast<LambdaExpr>(Arg)) {
152 LambdaOwnerMap.insert(std::make_pair(VD, L));
153 ConstructToIgnore.insert(CE);
154 LambdasToIgnore.insert(L);
163 bool VisitDeclRefExpr(DeclRefExpr *DRE)
override {
164 if (DeclRefExprsToIgnore.contains(DRE))
166 auto *VD = dyn_cast_or_null<VarDecl>(DRE->
getDecl());
169 if (
auto It = LambdaOwnerMap.find(VD); It != LambdaOwnerMap.end()) {
170 auto *L = It->second;
171 Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
178 auto *L = dyn_cast_or_null<LambdaExpr>(
Init->IgnoreParenCasts());
181 LambdasToIgnore.insert(L);
182 Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
187 bool shouldTreatAllArgAsNoEscape(FunctionDecl *FDecl) {
189 for (
auto *Decl = FDecl->
getParent(); Decl; Decl =
Decl->getParent()) {
196 if (Name ==
"WTF" && PreviousName ==
"switchOn")
199 if (Name ==
"std" && PreviousName ==
"ranges")
206 bool VisitCXXConstructExpr(CXXConstructExpr *CE)
override {
207 if (ConstructToIgnore.contains(CE))
210 unsigned ArgIndex = 0;
211 for (
auto *Param :
Callee->parameters()) {
215 if (
auto *L = findLambdaInArg(Arg)) {
216 LambdasToIgnore.insert(L);
217 if (!Param->hasAttr<NoEscapeAttr>())
218 Checker->visitLambdaExpr(
219 L, shouldCheckThis() && !hasProtectedThis(L), ClsType);
227 bool VisitCallExpr(CallExpr *CE)
override {
228 if (CallToIgnore.contains(CE))
230 checkCalleeLambda(CE);
232 if (isVisitFunction(CE, Callee))
234 checkParameters(CE, Callee);
235 }
else if (
auto *CalleeE = CE->
getCallee()) {
236 if (
auto *DRE = dyn_cast<DeclRefExpr>(CalleeE->IgnoreParenCasts())) {
237 if (
auto *Callee = dyn_cast_or_null<FunctionDecl>(DRE->
getDecl()))
238 checkParameters(CE, Callee);
244 bool isVisitFunction(CallExpr *CallExpr, FunctionDecl *FnDecl) {
255 if (NsName !=
"WTF" && NsName !=
"std")
257 auto *Arg = CallExpr->
getArg(0);
260 auto *DRE = dyn_cast<DeclRefExpr>(Arg->IgnoreParenCasts());
263 auto *VD = dyn_cast<VarDecl>(DRE->
getDecl());
266 if (!LambdaOwnerMap.contains(VD))
268 DeclRefExprsToIgnore.insert(DRE);
272 void checkParameters(CallExpr *CE, FunctionDecl *Callee) {
274 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(Callee);
275 for (
auto *Param :
Callee->parameters()) {
279 if (
auto *L = findLambdaInArg(Arg)) {
280 LambdasToIgnore.insert(L);
281 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
282 Checker->visitLambdaExpr(
283 L, shouldCheckThis() && !hasProtectedThis(L), ClsType);
290 if (
auto *Lambda = dyn_cast_or_null<LambdaExpr>(E))
292 auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(E);
298 if (
auto *Lambda = dyn_cast<LambdaExpr>(E))
300 auto *CE = dyn_cast_or_null<CXXConstructExpr>(E);
306 auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(CtorArg);
307 if (InnerCE && InnerCE->getNumArgs())
308 CtorArg = InnerCE->getArg(0)->IgnoreParenCasts();
309 auto updateIgnoreList = [&] {
310 ConstructToIgnore.insert(CE);
312 ConstructToIgnore.insert(InnerCE);
314 if (
auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
318 if (
auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(CtorArg)) {
320 if (
auto *Lambda = dyn_cast<LambdaExpr>(E)) {
325 auto *DRE = dyn_cast<DeclRefExpr>(CtorArg);
328 auto *VD = dyn_cast_or_null<VarDecl>(DRE->
getDecl());
334 if (
auto *Lambda = dyn_cast<LambdaExpr>(
Init)) {
335 DeclRefExprsToIgnore.insert(DRE);
342 void checkCalleeLambda(CallExpr *CE) {
346 auto *DRE = dyn_cast<DeclRefExpr>(
Callee->IgnoreParenCasts());
349 auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->
getDecl());
353 if (
auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
354 LambdasToIgnore.insert(L);
357 auto *ArgRef = dyn_cast<DeclRefExpr>(Arg);
360 auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl());
366 auto *L = dyn_cast_or_null<LambdaExpr>(
Init->IgnoreParenCasts());
369 DeclRefExprsToIgnore.insert(ArgRef);
370 LambdasToIgnore.insert(L);
374 for (
const LambdaCapture &OtherCapture : L->
captures()) {
375 if (!OtherCapture.capturesVariable())
377 if (
auto *ValueDecl = OtherCapture.getCapturedVar()) {
378 if (declProtectsThis(ValueDecl)) {
379 ProtectedThisDecls.insert(ValueDecl);
387 bool declProtectsThis(
const ValueDecl *ValueDecl)
const {
388 auto *VD = dyn_cast<VarDecl>(ValueDecl);
396 if (
auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Arg))
398 if (
auto *CE = dyn_cast<CXXConstructExpr>(Arg)) {
399 auto *Ctor = CE->getConstructor();
403 if (Checker->isPtrType(clsName) && CE->
getNumArgs()) {
408 if (
auto *CXXR =
Type->getPointeeCXXRecordDecl()) {
409 if (CXXR == Ctor->getParent() && Ctor->isMoveConstructor() &&
418 if (
auto *CE = dyn_cast<CallExpr>(Arg)) {
430 if (
auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Arg)) {
431 auto OpCode = OpCE->getOperator();
432 if (OpCode == OO_Star || OpCode == OO_Amp) {
433 auto *
Callee = OpCE->getDirectCallee();
437 if (!Checker->isPtrType(clsName) || !OpCE->getNumArgs())
443 if (
auto *UO = dyn_cast<UnaryOperator>(Arg)) {
444 auto OpCode = UO->getOpcode();
445 if (OpCode == UO_Deref || OpCode == UO_AddrOf) {
452 if (
auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
454 if (
auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Decl)) {
455 auto kind = ImplicitParam->getParameterKind();
456 return kind == ImplicitParamKind::ObjCSelf ||
457 kind == ImplicitParamKind::CXXThis;
459 return ProtectedThisDecls.contains(Decl);
465 LocalVisitor visitor(
this);
467 RTC->visitTranslationUnitDecl(TUD);
468 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
471 void visitLambdaExpr(
const LambdaExpr *L,
bool shouldCheckThis,
473 bool ignoreParamVarDecl =
false)
const {
474 if (TFA.isTrivial(L->
getBody()))
476 for (
const LambdaCapture &
C : L->
captures()) {
477 if (
C.capturesVariable()) {
478 ValueDecl *CapturedVar =
C.getCapturedVar();
481 if (
auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(CapturedVar)) {
482 auto kind = ImplicitParam->getParameterKind();
483 if ((kind == ImplicitParamKind::ObjCSelf ||
484 kind == ImplicitParamKind::CXXThis) &&
488 QualType CapturedVarQualType = CapturedVar->
getType();
493 if (IsUncountedPtr && *IsUncountedPtr)
494 reportBug(
C, CapturedVar, CapturedVarQualType, L);
495 }
else if (
C.capturesThis() && shouldCheckThis) {
496 if (ignoreParamVarDecl)
498 reportBugOnThisPtr(
C,
T);
503 void reportBug(
const LambdaCapture &
Capture, ValueDecl *CapturedVar,
507 auto Location =
Capture.getLocation();
511 SmallString<100> Buf;
512 llvm::raw_svector_ostream Os(Buf);
517 Os <<
"Implicitly captured ";
520 Os <<
"raw-pointer ";
526 Os <<
" to " << ptrKind(
T) <<
" type is unsafe.";
528 PathDiagnosticLocation BSLoc(Location, BR->getSourceManager());
529 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
530 BR->emitReport(std::move(
Report));
533 void reportBugOnThisPtr(
const LambdaCapture &
Capture,
534 const QualType
T)
const {
535 SmallString<100> Buf;
536 llvm::raw_svector_ostream Os(Buf);
541 Os <<
"Implicitly captured ";
544 Os <<
"raw-pointer 'this' to " << ptrKind(
T) <<
" type is unsafe.";
546 PathDiagnosticLocation BSLoc(
Capture.getLocation(), BR->getSourceManager());
547 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
548 BR->emitReport(std::move(
Report));
552class UncountedLambdaCapturesChecker :
public RawPtrRefLambdaCapturesChecker {
554 UncountedLambdaCapturesChecker()
555 : RawPtrRefLambdaCapturesChecker(
"Lambda capture of uncounted or "
556 "unchecked variable") {}
558 std::optional<bool>
isUnsafePtr(QualType QT)
const final {
561 if (result1 && *result1)
563 if (result2 && *result2)
570 virtual bool isPtrType(
const std::string &Name)
const final {
574 const char *ptrKind(QualType QT)
const final {
581class UnretainedLambdaCapturesChecker :
public RawPtrRefLambdaCapturesChecker {
583 UnretainedLambdaCapturesChecker()
584 : RawPtrRefLambdaCapturesChecker(
"Lambda capture of unretained "
586 RTC = RetainTypeChecker();
589 std::optional<bool>
isUnsafePtr(QualType QT)
const final {
590 return RTC->isUnretained(QT);
593 virtual bool isPtrType(
const std::string &Name)
const final {
597 const char *ptrKind(QualType QT)
const final {
return "unretained"; }
602void ento::registerUncountedLambdaCapturesChecker(
CheckerManager &Mgr) {
606bool ento::shouldRegisterUncountedLambdaCapturesChecker(
611void ento::registerUnretainedLambdaCapturesChecker(
CheckerManager &Mgr) {
615bool ento::shouldRegisterUnretainedLambdaCapturesChecker(
Expr * getArg(unsigned Arg)
Return the specified argument.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
QualType getThisType() const
Return the type of the this pointer.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
bool isCallToStdMove() const
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Stmt * getBody() const
Retrieve the body of the lambda.
SourceLocation getBeginLoc() const LLVM_READONLY
capture_range captures() const
Retrieve this lambda's captures.
ImplicitParamDecl * getSelfDecl() const
bool isInstanceMethod() const
bool isNull() const
Return true if this QualType doesn't point to a type yet.
const Type * getTypePtrOrNull() const
bool isReferenceType() const
const Expr * getInit() const
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
unsigned kind
All of the diagnostics that can be emitted by the frontend.
constexpr bool isPtrType(PrimType T)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
bool isCtorOfSafePtr(const clang::FunctionDecl *F)
bool isa(CodeGen::Address addr)
@ LCK_ByCopy
Capturing by copy (a.k.a., by value)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
std::optional< bool > isUnsafePtr(const QualType T, bool IsArcEnabled)
const FunctionProtoType * T
@ Type
The name was classified as a type.
bool isRetainPtrOrOSPtr(const std::string &Name)
bool isRefType(const std::string &Name)
std::optional< bool > isUncountedPtr(const QualType T)
std::string safeGetName(const T *ASTNode)
bool isCheckedPtr(const std::string &Name)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
std::optional< bool > isUncounted(const QualType T)
std::optional< bool > isUncheckedPtr(const QualType T)