Skip to content

Commit e869103

Browse files
authored
[webkit.UncountedLambdaCapturesChecker] Add a fallback for checking lambda captures (#119800)
1 parent 7168de5 commit e869103

File tree

2 files changed

+111
-12
lines changed

2 files changed

+111
-12
lines changed

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class UncountedLambdaCapturesChecker
4040
struct LocalVisitor : DynamicRecursiveASTVisitor {
4141
const UncountedLambdaCapturesChecker *Checker;
4242
llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
43+
llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
4344
QualType ClsType;
4445

4546
explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker)
@@ -61,6 +62,24 @@ class UncountedLambdaCapturesChecker
6162
return result && *result;
6263
}
6364

65+
bool VisitLambdaExpr(LambdaExpr *L) override {
66+
if (LambdasToIgnore.contains(L))
67+
return true;
68+
Checker->visitLambdaExpr(L, shouldCheckThis());
69+
return true;
70+
}
71+
72+
bool VisitVarDecl(VarDecl *VD) override {
73+
auto *Init = VD->getInit();
74+
if (!Init)
75+
return true;
76+
auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts());
77+
if (!L)
78+
return true;
79+
LambdasToIgnore.insert(L); // Evaluate lambdas in VisitDeclRefExpr.
80+
return true;
81+
}
82+
6483
bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
6584
if (DeclRefExprsToIgnore.contains(DRE))
6685
return true;
@@ -73,6 +92,7 @@ class UncountedLambdaCapturesChecker
7392
auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts());
7493
if (!L)
7594
return true;
95+
LambdasToIgnore.insert(L);
7696
Checker->visitLambdaExpr(L, shouldCheckThis());
7797
return true;
7898
}
@@ -95,10 +115,10 @@ class UncountedLambdaCapturesChecker
95115
if (ArgIndex >= CE->getNumArgs())
96116
return true;
97117
auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();
98-
if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape) {
99-
if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
118+
if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
119+
LambdasToIgnore.insert(L);
120+
if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
100121
Checker->visitLambdaExpr(L, shouldCheckThis());
101-
}
102122
}
103123
++ArgIndex;
104124
}
@@ -117,6 +137,10 @@ class UncountedLambdaCapturesChecker
117137
if (!MD || CE->getNumArgs() < 1)
118138
return;
119139
auto *Arg = CE->getArg(0)->IgnoreParenCasts();
140+
if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
141+
LambdasToIgnore.insert(L); // Calling a lambda upon creation is safe.
142+
return;
143+
}
120144
auto *ArgRef = dyn_cast<DeclRefExpr>(Arg);
121145
if (!ArgRef)
122146
return;
@@ -130,6 +154,7 @@ class UncountedLambdaCapturesChecker
130154
if (!L)
131155
return;
132156
DeclRefExprsToIgnore.insert(ArgRef);
157+
LambdasToIgnore.insert(L);
133158
Checker->visitLambdaExpr(L, shouldCheckThis(),
134159
/* ignoreParamVarDecl */ true);
135160
}

clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,72 @@
11
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.UncountedLambdaCapturesChecker -verify %s
22

3-
struct A {
4-
static void b();
3+
#include "mock-types.h"
4+
5+
namespace WTF {
6+
7+
namespace Detail {
8+
9+
template<typename Out, typename... In>
10+
class CallableWrapperBase {
11+
public:
12+
virtual ~CallableWrapperBase() { }
13+
virtual Out call(In...) = 0;
14+
};
15+
16+
template<typename, typename, typename...> class CallableWrapper;
17+
18+
template<typename CallableType, typename Out, typename... In>
19+
class CallableWrapper : public CallableWrapperBase<Out, In...> {
20+
public:
21+
explicit CallableWrapper(CallableType& callable)
22+
: m_callable(callable) { }
23+
Out call(In... in) final { return m_callable(in...); }
24+
25+
private:
26+
CallableType m_callable;
27+
};
28+
29+
} // namespace Detail
30+
31+
template<typename> class Function;
32+
33+
template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>*);
34+
35+
template <typename Out, typename... In>
36+
class Function<Out(In...)> {
37+
public:
38+
using Impl = Detail::CallableWrapperBase<Out, In...>;
39+
40+
Function() = default;
41+
42+
template<typename FunctionType>
43+
Function(FunctionType f)
44+
: m_callableWrapper(new Detail::CallableWrapper<FunctionType, Out, In...>(f)) { }
45+
46+
Out operator()(In... in) const { return m_callableWrapper->call(in...); }
47+
explicit operator bool() const { return !!m_callableWrapper; }
48+
49+
private:
50+
enum AdoptTag { Adopt };
51+
Function(Impl* impl, AdoptTag)
52+
: m_callableWrapper(impl)
53+
{
54+
}
55+
56+
friend Function adopt<Out, In...>(Impl*);
57+
58+
std::unique_ptr<Impl> m_callableWrapper;
559
};
660

7-
struct RefCountable {
8-
void ref() {}
9-
void deref() {}
10-
void method();
11-
void constMethod() const;
12-
int trivial() { return 123; }
13-
RefCountable* next();
61+
template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>* impl)
62+
{
63+
return Function<Out(In...)>(impl, Function<Out(In...)>::Adopt);
64+
}
65+
66+
} // namespace WTF
67+
68+
struct A {
69+
static void b();
1470
};
1571

1672
RefCountable* make_obj();
@@ -185,3 +241,21 @@ void lambda_with_args(RefCountable* obj) {
185241
};
186242
trivial_lambda(1);
187243
}
244+
245+
void callFunctionOpaque(WTF::Function<void()>&&);
246+
void callFunction(WTF::Function<void()>&& function) {
247+
someFunction();
248+
function();
249+
}
250+
251+
void lambda_converted_to_function(RefCountable* obj)
252+
{
253+
callFunction([&]() {
254+
obj->method();
255+
// expected-warning@-1{{Implicitly captured raw-pointer 'obj' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
256+
});
257+
callFunctionOpaque([&]() {
258+
obj->method();
259+
// expected-warning@-1{{Implicitly captured raw-pointer 'obj' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
260+
});
261+
}

0 commit comments

Comments
 (0)