diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp index 599c2179db0f0..ac5cf3d899d55 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 DeclRefExprsToIgnore; + llvm::DenseSet 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(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(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() && !TreatAllArgsAsNoEscape) { - if (auto *L = dyn_cast_or_null(Arg)) { + if (auto *L = dyn_cast_or_null(Arg)) { + LambdasToIgnore.insert(L); + if (!Param->hasAttr() && !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(Arg)) { + LambdasToIgnore.insert(L); // Calling a lambda upon creation is safe. + return; + } auto *ArgRef = dyn_cast(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 65eee9d49106d..daff32e9940c8 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 +class CallableWrapperBase { +public: + virtual ~CallableWrapperBase() { } + virtual Out call(In...) = 0; +}; + +template class CallableWrapper; + +template +class CallableWrapper : public CallableWrapperBase { +public: + explicit CallableWrapper(CallableType& callable) + : m_callable(callable) { } + Out call(In... in) final { return m_callable(in...); } + +private: + CallableType m_callable; +}; + +} // namespace Detail + +template class Function; + +template Function adopt(Detail::CallableWrapperBase*); + +template +class Function { +public: + using Impl = Detail::CallableWrapperBase; + + Function() = default; + + template + Function(FunctionType f) + : m_callableWrapper(new Detail::CallableWrapper(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(Impl*); + + std::unique_ptr m_callableWrapper; }; -struct RefCountable { - void ref() {} - void deref() {} - void method(); - void constMethod() const; - int trivial() { return 123; } - RefCountable* next(); +template Function adopt(Detail::CallableWrapperBase* impl) +{ + return Function(impl, Function::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 callFunction(WTF::Function&& 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]}} + }); +}