Skip to content

Commit

Permalink
[webkit.UncountedLambdaCapturesChecker] Add a fallback for checking l…
Browse files Browse the repository at this point in the history
…ambda captures (#119800)
  • Loading branch information
rniwa authored Dec 15, 2024
1 parent 7168de5 commit e869103
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class UncountedLambdaCapturesChecker
struct LocalVisitor : DynamicRecursiveASTVisitor {
const UncountedLambdaCapturesChecker *Checker;
llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
QualType ClsType;

explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker)
Expand All @@ -61,6 +62,24 @@ class UncountedLambdaCapturesChecker
return result && *result;
}

bool VisitLambdaExpr(LambdaExpr *L) override {
if (LambdasToIgnore.contains(L))
return true;
Checker->visitLambdaExpr(L, shouldCheckThis());
return true;
}

bool VisitVarDecl(VarDecl *VD) override {
auto *Init = VD->getInit();
if (!Init)
return true;
auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts());
if (!L)
return true;
LambdasToIgnore.insert(L); // Evaluate lambdas in VisitDeclRefExpr.
return true;
}

bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
if (DeclRefExprsToIgnore.contains(DRE))
return true;
Expand All @@ -73,6 +92,7 @@ class UncountedLambdaCapturesChecker
auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts());
if (!L)
return true;
LambdasToIgnore.insert(L);
Checker->visitLambdaExpr(L, shouldCheckThis());
return true;
}
Expand All @@ -95,10 +115,10 @@ class UncountedLambdaCapturesChecker
if (ArgIndex >= CE->getNumArgs())
return true;
auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();
if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape) {
if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
LambdasToIgnore.insert(L);
if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
Checker->visitLambdaExpr(L, shouldCheckThis());
}
}
++ArgIndex;
}
Expand All @@ -117,6 +137,10 @@ class UncountedLambdaCapturesChecker
if (!MD || CE->getNumArgs() < 1)
return;
auto *Arg = CE->getArg(0)->IgnoreParenCasts();
if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
LambdasToIgnore.insert(L); // Calling a lambda upon creation is safe.
return;
}
auto *ArgRef = dyn_cast<DeclRefExpr>(Arg);
if (!ArgRef)
return;
Expand All @@ -130,6 +154,7 @@ class UncountedLambdaCapturesChecker
if (!L)
return;
DeclRefExprsToIgnore.insert(ArgRef);
LambdasToIgnore.insert(L);
Checker->visitLambdaExpr(L, shouldCheckThis(),
/* ignoreParamVarDecl */ true);
}
Expand Down
92 changes: 83 additions & 9 deletions clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,72 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.UncountedLambdaCapturesChecker -verify %s

struct A {
static void b();
#include "mock-types.h"

namespace WTF {

namespace Detail {

template<typename Out, typename... In>
class CallableWrapperBase {
public:
virtual ~CallableWrapperBase() { }
virtual Out call(In...) = 0;
};

template<typename, typename, typename...> class CallableWrapper;

template<typename CallableType, typename Out, typename... In>
class CallableWrapper : public CallableWrapperBase<Out, In...> {
public:
explicit CallableWrapper(CallableType& callable)
: m_callable(callable) { }
Out call(In... in) final { return m_callable(in...); }

private:
CallableType m_callable;
};

} // namespace Detail

template<typename> class Function;

template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>*);

template <typename Out, typename... In>
class Function<Out(In...)> {
public:
using Impl = Detail::CallableWrapperBase<Out, In...>;

Function() = default;

template<typename FunctionType>
Function(FunctionType f)
: m_callableWrapper(new Detail::CallableWrapper<FunctionType, Out, In...>(f)) { }

Out operator()(In... in) const { return m_callableWrapper->call(in...); }
explicit operator bool() const { return !!m_callableWrapper; }

private:
enum AdoptTag { Adopt };
Function(Impl* impl, AdoptTag)
: m_callableWrapper(impl)
{
}

friend Function adopt<Out, In...>(Impl*);

std::unique_ptr<Impl> m_callableWrapper;
};

struct RefCountable {
void ref() {}
void deref() {}
void method();
void constMethod() const;
int trivial() { return 123; }
RefCountable* next();
template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>* impl)
{
return Function<Out(In...)>(impl, Function<Out(In...)>::Adopt);
}

} // namespace WTF

struct A {
static void b();
};

RefCountable* make_obj();
Expand Down Expand Up @@ -185,3 +241,21 @@ void lambda_with_args(RefCountable* obj) {
};
trivial_lambda(1);
}

void callFunctionOpaque(WTF::Function<void()>&&);
void callFunction(WTF::Function<void()>&& function) {
someFunction();
function();
}

void lambda_converted_to_function(RefCountable* obj)
{
callFunction([&]() {
obj->method();
// expected-warning@-1{{Implicitly captured raw-pointer 'obj' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
});
callFunctionOpaque([&]() {
obj->method();
// expected-warning@-1{{Implicitly captured raw-pointer 'obj' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
});
}

0 comments on commit e869103

Please sign in to comment.