Skip to content

Commit 52215bb

Browse files
committed
[Analyzer][WebKit] UncountedLambdaCaptureChecker
Differential Revision: https://reviews.llvm.org/D82837
1 parent 6d38d88 commit 52215bb

File tree

5 files changed

+174
-0
lines changed

5 files changed

+174
-0
lines changed

clang/docs/analyzer/checkers.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,25 @@ Raw pointers and references to uncounted types can't be used as class members. O
14231423
// ...
14241424
};
14251425
1426+
.. _webkit-UncountedLambdaCapturesChecker:
1427+
1428+
webkit.UncountedLambdaCapturesChecker
1429+
"""""""""""""""""""""""""""""""""""""
1430+
Raw pointers and references to uncounted types can't be captured in lambdas. Only ref-counted types are allowed.
1431+
1432+
.. code-block:: cpp
1433+
1434+
struct RefCntbl {
1435+
void ref() {}
1436+
void deref() {}
1437+
};
1438+
1439+
void foo(RefCntbl* a, RefCntbl& b) {
1440+
[&, a](){ // warn about 'a'
1441+
do_something(b); // warn about 'b'
1442+
};
1443+
};
1444+
14261445
.. _alpha-checkers:
14271446
14281447
Experimental Checkers

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,6 +1654,10 @@ def NoUncountedMemberChecker : Checker<"NoUncountedMemberChecker">,
16541654
HelpText<"Check for no uncounted member variables.">,
16551655
Documentation<HasDocumentation>;
16561656

1657+
def UncountedLambdaCapturesChecker : Checker<"UncountedLambdaCapturesChecker">,
1658+
HelpText<"Check uncounted lambda captures.">,
1659+
Documentation<HasDocumentation>;
1660+
16571661
} // end webkit
16581662

16591663
let ParentPackage = WebKitAlpha in {

clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ add_clang_library(clangStaticAnalyzerCheckers
127127
WebKit/PtrTypesSemantics.cpp
128128
WebKit/RefCntblBaseVirtualDtorChecker.cpp
129129
WebKit/UncountedCallArgsChecker.cpp
130+
WebKit/UncountedLambdaCapturesChecker.cpp
130131

131132
LINK_LIBS
132133
clangAST
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//=======- UncountedLambdaCapturesChecker.cpp --------------------*- C++ -*-==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DiagOutputUtils.h"
10+
#include "PtrTypesSemantics.h"
11+
#include "clang/AST/CXXInheritance.h"
12+
#include "clang/AST/RecursiveASTVisitor.h"
13+
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14+
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
15+
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16+
#include "clang/StaticAnalyzer/Core/Checker.h"
17+
18+
using namespace clang;
19+
using namespace ento;
20+
21+
namespace {
22+
class UncountedLambdaCapturesChecker
23+
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
24+
private:
25+
BugType Bug{this, "Lambda capture of uncounted variable",
26+
"WebKit coding guidelines"};
27+
mutable BugReporter *BR;
28+
29+
public:
30+
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
31+
BugReporter &BRArg) const {
32+
BR = &BRArg;
33+
34+
// The calls to checkAST* from AnalysisConsumer don't
35+
// visit template instantiations or lambda classes. We
36+
// want to visit those, so we make our own RecursiveASTVisitor.
37+
struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
38+
const UncountedLambdaCapturesChecker *Checker;
39+
explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker)
40+
: Checker(Checker) {
41+
assert(Checker);
42+
}
43+
44+
bool shouldVisitTemplateInstantiations() const { return true; }
45+
bool shouldVisitImplicitCode() const { return false; }
46+
47+
bool VisitLambdaExpr(LambdaExpr *L) {
48+
Checker->visitLambdaExpr(L);
49+
return true;
50+
}
51+
};
52+
53+
LocalVisitor visitor(this);
54+
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
55+
}
56+
57+
void visitLambdaExpr(LambdaExpr *L) const {
58+
for (const LambdaCapture &C : L->captures()) {
59+
if (C.capturesVariable()) {
60+
VarDecl *CapturedVar = C.getCapturedVar();
61+
if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) {
62+
if (isUncountedPtr(CapturedVarType)) {
63+
reportBug(C, CapturedVar, CapturedVarType);
64+
}
65+
}
66+
}
67+
}
68+
}
69+
70+
void reportBug(const LambdaCapture &Capture, VarDecl *CapturedVar,
71+
const Type *T) const {
72+
assert(CapturedVar);
73+
74+
SmallString<100> Buf;
75+
llvm::raw_svector_ostream Os(Buf);
76+
77+
if (Capture.isExplicit()) {
78+
Os << "Captured ";
79+
} else {
80+
Os << "Implicitly captured ";
81+
}
82+
if (T->isPointerType()) {
83+
Os << "raw-pointer ";
84+
} else {
85+
assert(T->isReferenceType());
86+
Os << "reference ";
87+
}
88+
89+
printQuotedQualifiedName(Os, Capture.getCapturedVar());
90+
Os << " to uncounted type is unsafe.";
91+
92+
PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
93+
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
94+
BR->emitReport(std::move(Report));
95+
}
96+
};
97+
} // namespace
98+
99+
void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {
100+
Mgr.registerChecker<UncountedLambdaCapturesChecker>();
101+
}
102+
103+
bool ento::shouldRegisterUncountedLambdaCapturesChecker(
104+
const CheckerManager &mgr) {
105+
return true;
106+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.UncountedLambdaCapturesChecker %s 2>&1 | FileCheck %s --strict-whitespace
2+
#include "mock-types.h"
3+
4+
void raw_ptr() {
5+
RefCountable* ref_countable = nullptr;
6+
auto foo1 = [ref_countable](){};
7+
// CHECK: warning: Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]
8+
// CHECK-NEXT:{{^}} auto foo1 = [ref_countable](){};
9+
// CHECK-NEXT:{{^}} ^
10+
auto foo2 = [&ref_countable](){};
11+
// CHECK: warning: Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]
12+
auto foo3 = [&](){ ref_countable = nullptr; };
13+
// CHECK: warning: Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]
14+
// CHECK-NEXT:{{^}} auto foo3 = [&](){ ref_countable = nullptr; };
15+
// CHECK-NEXT:{{^}} ^
16+
auto foo4 = [=](){ (void) ref_countable; };
17+
// CHECK: warning: Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]
18+
}
19+
20+
void references() {
21+
RefCountable automatic;
22+
RefCountable& ref_countable_ref = automatic;
23+
24+
auto foo1 = [ref_countable_ref](){};
25+
// CHECK: warning: Captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]
26+
auto foo2 = [&ref_countable_ref](){};
27+
// CHECK: warning: Captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]
28+
auto foo3 = [&](){ (void) ref_countable_ref; };
29+
// CHECK: warning: Implicitly captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]
30+
auto foo4 = [=](){ (void) ref_countable_ref; };
31+
// CHECK: warning: Implicitly captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]
32+
}
33+
34+
void quiet() {
35+
// This code is not expected to trigger any warnings.
36+
{
37+
RefCountable automatic;
38+
RefCountable &ref_countable_ref = automatic;
39+
}
40+
41+
auto foo3 = [&]() {};
42+
auto foo4 = [=]() {};
43+
RefCountable *ref_countable = nullptr;
44+
}

0 commit comments

Comments
 (0)