-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[webkit.UncountedLambdaCapturesChecker] Add a fallback for checking lambda captures #119800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ambda captures This PR reintroduces VisitLambdaExpr in LocalVisitor so that lambdas used to constrcut WTF::Function, for example, would get checked for its lambda captures. We explicitly ignore lambda used in initializing a VarDecl, and lambdas we evaluated with other means via newly introduced DenseSet, LambdasToIgnore.
@llvm/pr-subscribers-clang-static-analyzer-1 Author: Ryosuke Niwa (rniwa) ChangesThis PR reintroduces VisitLambdaExpr in LocalVisitor so that lambdas used to constrcut WTF::Function, for example, would get checked for its lambda captures. We explicitly ignore lambda used in initializing a VarDecl, and lambdas we evaluated with other means via newly introduced DenseSet, LambdasToIgnore. Full diff: https://github.com/llvm/llvm-project/pull/119800.diff 2 Files Affected:
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
index 599c2179db0f0e..ac5cf3d899d55a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -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)
@@ -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;
@@ -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;
}
@@ -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;
}
@@ -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;
@@ -130,6 +154,7 @@ class UncountedLambdaCapturesChecker
if (!L)
return;
DeclRefExprsToIgnore.insert(ArgRef);
+ LambdasToIgnore.insert(L);
Checker->visitLambdaExpr(L, shouldCheckThis(),
/* ignoreParamVarDecl */ true);
}
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
index 65eee9d49106df..daff32e9940c83 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
@@ -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();
@@ -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]}}
+ });
+}
|
@llvm/pr-subscribers-clang Author: Ryosuke Niwa (rniwa) ChangesThis PR reintroduces VisitLambdaExpr in LocalVisitor so that lambdas used to constrcut WTF::Function, for example, would get checked for its lambda captures. We explicitly ignore lambda used in initializing a VarDecl, and lambdas we evaluated with other means via newly introduced DenseSet, LambdasToIgnore. Full diff: https://github.com/llvm/llvm-project/pull/119800.diff 2 Files Affected:
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
index 599c2179db0f0e..ac5cf3d899d55a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -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)
@@ -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;
@@ -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;
}
@@ -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;
}
@@ -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;
@@ -130,6 +154,7 @@ class UncountedLambdaCapturesChecker
if (!L)
return;
DeclRefExprsToIgnore.insert(ArgRef);
+ LambdasToIgnore.insert(L);
Checker->visitLambdaExpr(L, shouldCheckThis(),
/* ignoreParamVarDecl */ true);
}
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
index 65eee9d49106df..daff32e9940c83 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
@@ -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();
@@ -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]}}
+ });
+}
|
auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); | ||
if (!L) | ||
return true; | ||
LambdasToIgnore.insert(L); // Evaluate lambdas in VisitDeclRefExpr. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand this comment. L
is a lambda that should be ignored. Is this saying it will be evaluated in VisitDeclRefExpr
again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I'm saying that this lambda will be checked inside VisitDeclRefExpr.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Great. Thanks for the review. |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/73/builds/10366 Here is the relevant piece of the build log for the reference
|
This PR reintroduces VisitLambdaExpr in LocalVisitor so that lambdas used to constrcut WTF::Function, for example, would get checked for its lambda captures.
We explicitly ignore lambda used in initializing a VarDecl, and lambdas we evaluated with other means via newly introduced DenseSet, LambdasToIgnore.