Skip to content

Commit 81d9f2f

Browse files
rniwaRyosuke Niwa
authored andcommitted
[alpha.webkit.UnretainedLambdaCapturesChecker] Add the support for protectedSelf (llvm#132518)
This PR adds the support for treating capturing of "self" as safe if the lambda simultaneously captures "protectedSelf", which is a RetainPtr of "self". This PR also fixes a bug that the checker wasn't generating a warning when "self" is implicitly captured. Note when "self" is implicitly captured, we use the lambda's getBeginLoc as a fallback source location.
1 parent fcfed41 commit 81d9f2f

File tree

4 files changed

+95
-14
lines changed

4 files changed

+95
-14
lines changed

clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ bool isCtorOfCheckedPtr(const clang::FunctionDecl *F) {
147147
bool isCtorOfRetainPtr(const clang::FunctionDecl *F) {
148148
const std::string &FunctionName = safeGetName(F);
149149
return FunctionName == "RetainPtr" || FunctionName == "adoptNS" ||
150-
FunctionName == "adoptCF";
150+
FunctionName == "adoptCF" || FunctionName == "retainPtr";
151151
}
152152

153153
bool isCtorOfSafePtr(const clang::FunctionDecl *F) {

clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class RawPtrRefLambdaCapturesChecker
3636
: Bug(this, description, "WebKit coding guidelines") {}
3737

3838
virtual std::optional<bool> isUnsafePtr(QualType) const = 0;
39+
virtual bool isPtrType(const std::string &) const = 0;
3940
virtual const char *ptrKind(QualType QT) const = 0;
4041

4142
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -71,6 +72,15 @@ class RawPtrRefLambdaCapturesChecker
7172
return Base::TraverseCXXMethodDecl(CXXMD);
7273
}
7374

75+
bool TraverseObjCMethodDecl(ObjCMethodDecl *OCMD) {
76+
llvm::SaveAndRestore SavedDecl(ClsType);
77+
if (OCMD && OCMD->isInstanceMethod()) {
78+
if (auto *ImplParamDecl = OCMD->getSelfDecl())
79+
ClsType = ImplParamDecl->getType();
80+
}
81+
return Base::TraverseObjCMethodDecl(OCMD);
82+
}
83+
7484
bool VisitTypedefDecl(TypedefDecl *TD) {
7585
if (Checker->RTC)
7686
Checker->RTC->visitTypedef(TD);
@@ -278,10 +288,10 @@ class RawPtrRefLambdaCapturesChecker
278288
auto *VD = dyn_cast<VarDecl>(ValueDecl);
279289
if (!VD)
280290
return false;
281-
auto *Init = VD->getInit()->IgnoreParenCasts();
291+
auto *Init = VD->getInit();
282292
if (!Init)
283293
return false;
284-
const Expr *Arg = Init;
294+
const Expr *Arg = Init->IgnoreParenCasts();
285295
do {
286296
if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Arg))
287297
Arg = BTE->getSubExpr()->IgnoreParenCasts();
@@ -290,7 +300,7 @@ class RawPtrRefLambdaCapturesChecker
290300
if (!Ctor)
291301
return false;
292302
auto clsName = safeGetName(Ctor->getParent());
293-
if (isRefType(clsName) && CE->getNumArgs()) {
303+
if (Checker->isPtrType(clsName) && CE->getNumArgs()) {
294304
Arg = CE->getArg(0)->IgnoreParenCasts();
295305
continue;
296306
}
@@ -310,6 +320,12 @@ class RawPtrRefLambdaCapturesChecker
310320
Arg = CE->getArg(0)->IgnoreParenCasts();
311321
continue;
312322
}
323+
if (auto *Callee = CE->getDirectCallee()) {
324+
if (isCtorOfSafePtr(Callee) && CE->getNumArgs() == 1) {
325+
Arg = CE->getArg(0)->IgnoreParenCasts();
326+
continue;
327+
}
328+
}
313329
}
314330
if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Arg)) {
315331
auto OpCode = OpCE->getOperator();
@@ -318,7 +334,7 @@ class RawPtrRefLambdaCapturesChecker
318334
if (!Callee)
319335
return false;
320336
auto clsName = safeGetName(Callee->getParent());
321-
if (!isRefType(clsName) || !OpCE->getNumArgs())
337+
if (!Checker->isPtrType(clsName) || !OpCE->getNumArgs())
322338
return false;
323339
Arg = OpCE->getArg(0)->IgnoreParenCasts();
324340
continue;
@@ -333,8 +349,15 @@ class RawPtrRefLambdaCapturesChecker
333349
}
334350
break;
335351
} while (Arg);
336-
if (auto *DRE = dyn_cast<DeclRefExpr>(Arg))
337-
return ProtectedThisDecls.contains(DRE->getDecl());
352+
if (auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
353+
auto *Decl = DRE->getDecl();
354+
if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Decl)) {
355+
auto kind = ImplicitParam->getParameterKind();
356+
return kind == ImplicitParamKind::ObjCSelf ||
357+
kind == ImplicitParamKind::CXXThis;
358+
}
359+
return ProtectedThisDecls.contains(Decl);
360+
}
338361
return isa<CXXThisExpr>(Arg);
339362
}
340363
};
@@ -354,10 +377,17 @@ class RawPtrRefLambdaCapturesChecker
354377
ValueDecl *CapturedVar = C.getCapturedVar();
355378
if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar))
356379
continue;
380+
if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(CapturedVar)) {
381+
auto kind = ImplicitParam->getParameterKind();
382+
if ((kind == ImplicitParamKind::ObjCSelf ||
383+
kind == ImplicitParamKind::CXXThis) &&
384+
!shouldCheckThis)
385+
continue;
386+
}
357387
QualType CapturedVarQualType = CapturedVar->getType();
358388
auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType());
359389
if (IsUncountedPtr && *IsUncountedPtr)
360-
reportBug(C, CapturedVar, CapturedVarQualType);
390+
reportBug(C, CapturedVar, CapturedVarQualType, L);
361391
} else if (C.capturesThis() && shouldCheckThis) {
362392
if (ignoreParamVarDecl) // this is always a parameter to this function.
363393
continue;
@@ -367,11 +397,12 @@ class RawPtrRefLambdaCapturesChecker
367397
}
368398

369399
void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
370-
const QualType T) const {
400+
const QualType T, LambdaExpr *L) const {
371401
assert(CapturedVar);
372402

373-
if (isa<ImplicitParamDecl>(CapturedVar) && !Capture.getLocation().isValid())
374-
return; // Ignore implicit captruing of self.
403+
auto Location = Capture.getLocation();
404+
if (isa<ImplicitParamDecl>(CapturedVar) && !Location.isValid())
405+
Location = L->getBeginLoc();
375406

376407
SmallString<100> Buf;
377408
llvm::raw_svector_ostream Os(Buf);
@@ -390,7 +421,7 @@ class RawPtrRefLambdaCapturesChecker
390421
printQuotedQualifiedName(Os, CapturedVar);
391422
Os << " to " << ptrKind(T) << " type is unsafe.";
392423

393-
PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
424+
PathDiagnosticLocation BSLoc(Location, BR->getSourceManager());
394425
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
395426
BR->emitReport(std::move(Report));
396427
}
@@ -432,6 +463,10 @@ class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
432463
return result2;
433464
}
434465

466+
virtual bool isPtrType(const std::string &Name) const final {
467+
return isRefType(Name) || isCheckedPtr(Name);
468+
}
469+
435470
const char *ptrKind(QualType QT) const final {
436471
if (isUncounted(QT))
437472
return "uncounted";
@@ -451,6 +486,10 @@ class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
451486
return RTC->isUnretained(QT);
452487
}
453488

489+
virtual bool isPtrType(const std::string &Name) const final {
490+
return isRetainPtr(Name);
491+
}
492+
454493
const char *ptrKind(QualType QT) const final { return "unretained"; }
455494
};
456495

clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ @interface ObjWithSelf : NSObject {
256256
RetainPtr<id> delegate;
257257
}
258258
-(void)doWork;
259+
-(void)doMoreWork;
259260
-(void)run;
260261
@end
261262

@@ -265,9 +266,28 @@ -(void)doWork {
265266
someFunction();
266267
[delegate doWork];
267268
};
269+
auto doMoreWork = [=] {
270+
someFunction();
271+
[delegate doWork];
272+
};
273+
auto doExtraWork = [&, protectedSelf = retainPtr(self)] {
274+
someFunction();
275+
[delegate doWork];
276+
};
268277
callFunctionOpaque(doWork);
278+
callFunctionOpaque(doMoreWork);
279+
callFunctionOpaque(doExtraWork);
269280
}
281+
282+
-(void)doMoreWork {
283+
auto doWork = [self, protectedSelf = retainPtr(self)] {
284+
someFunction();
285+
[self doWork];
286+
};
287+
callFunctionOpaque(doWork);
288+
}
289+
270290
-(void)run {
271291
someFunction();
272292
}
273-
@end
293+
@end

clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,18 +279,40 @@ @interface ObjWithSelf : NSObject {
279279
RetainPtr<id> delegate;
280280
}
281281
-(void)doWork;
282+
-(void)doMoreWork;
282283
-(void)run;
283284
@end
284285

285286
@implementation ObjWithSelf
286287
-(void)doWork {
287288
auto doWork = [&] {
289+
// expected-warning@-1{{Implicitly captured raw-pointer 'self' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
290+
someFunction();
291+
[delegate doWork];
292+
};
293+
auto doMoreWork = [=] {
294+
// expected-warning@-1{{Implicitly captured raw-pointer 'self' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
295+
someFunction();
296+
[delegate doWork];
297+
};
298+
auto doExtraWork = [&, protectedSelf = retainPtr(self)] {
288299
someFunction();
289300
[delegate doWork];
290301
};
291302
callFunctionOpaque(doWork);
303+
callFunctionOpaque(doMoreWork);
304+
callFunctionOpaque(doExtraWork);
292305
}
306+
307+
-(void)doMoreWork {
308+
auto doWork = [self, protectedSelf = retainPtr(self)] {
309+
someFunction();
310+
[self doWork];
311+
};
312+
callFunctionOpaque(doWork);
313+
}
314+
293315
-(void)run {
294316
someFunction();
295317
}
296-
@end
318+
@end

0 commit comments

Comments
 (0)