Bug 1839316: part 5) Guard the "fetchpriority" attribute behind a pref. r=kershaw...
[gecko.git] / build / clang-plugin / RefCountedInsideLambdaChecker.cpp
blob3256aa8f04ceb7fa050592c0569ee4821594b1ff
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "RefCountedInsideLambdaChecker.h"
6 #include "CustomMatchers.h"
8 RefCountedMap RefCountedClasses;
10 void RefCountedInsideLambdaChecker::registerMatchers(MatchFinder *AstMatcher) {
11 // We want to reject any code which captures a pointer to an object of a
12 // refcounted type, and then lets that value escape. As a primitive analysis,
13 // we reject any occurances of the lambda as a template parameter to a class
14 // (which could allow it to escape), as well as any presence of such a lambda
15 // in a return value (either from lambdas, or in c++14, auto functions).
17 // We check these lambdas' capture lists for raw pointers to refcounted types.
18 AstMatcher->addMatcher(functionDecl(returns(recordType(hasDeclaration(
19 cxxRecordDecl(isLambdaDecl()).bind("decl"))))),
20 this);
21 AstMatcher->addMatcher(lambdaExpr().bind("lambdaExpr"), this);
22 AstMatcher->addMatcher(
23 classTemplateSpecializationDecl(
24 hasAnyTemplateArgument(refersToType(recordType(
25 hasDeclaration(cxxRecordDecl(isLambdaDecl()).bind("decl")))))),
26 this);
29 void RefCountedInsideLambdaChecker::emitDiagnostics(SourceLocation Loc,
30 StringRef Name,
31 QualType Type) {
32 diag(Loc,
33 "Refcounted variable '%0' of type %1 cannot be captured by a lambda",
34 DiagnosticIDs::Error)
35 << Name << Type;
36 diag(Loc, "Please consider using a smart pointer", DiagnosticIDs::Note);
39 static bool IsKnownLive(const VarDecl *Var) {
40 const Stmt *Init = Var->getInit();
41 if (!Init) {
42 return false;
44 if (auto *Call = dyn_cast<CallExpr>(Init)) {
45 const FunctionDecl *Callee = Call->getDirectCallee();
46 return Callee && Callee->getName() == "MOZ_KnownLive";
48 return false;
51 void RefCountedInsideLambdaChecker::check(
52 const MatchFinder::MatchResult &Result) {
53 static DenseSet<const CXXRecordDecl *> CheckedDecls;
55 const CXXRecordDecl *Lambda = Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
57 if (const LambdaExpr *OuterLambda =
58 Result.Nodes.getNodeAs<LambdaExpr>("lambdaExpr")) {
59 const CXXMethodDecl *OpCall = OuterLambda->getCallOperator();
60 QualType ReturnTy = OpCall->getReturnType();
61 if (const CXXRecordDecl *Record = ReturnTy->getAsCXXRecordDecl()) {
62 Lambda = Record;
66 if (!Lambda || !Lambda->isLambda()) {
67 return;
70 // Don't report errors on the same declarations more than once.
71 if (CheckedDecls.count(Lambda)) {
72 return;
74 CheckedDecls.insert(Lambda);
76 bool StrongRefToThisCaptured = false;
78 for (const LambdaCapture &Capture : Lambda->captures()) {
79 // Check if any of the captures are ByRef. If they are, we have nothing to
80 // report, as it's OK to capture raw pointers to refcounted objects so long
81 // as the Lambda doesn't escape the current scope, which is required by
82 // ByRef captures already.
83 if (Capture.getCaptureKind() == LCK_ByRef) {
84 return;
87 // Check if this capture is byvalue, and captures a strong reference to
88 // this.
89 // XXX: Do we want to make sure that this type which we are capturing is a
90 // "Smart Pointer" somehow?
91 if (!StrongRefToThisCaptured && Capture.capturesVariable() &&
92 Capture.getCaptureKind() == LCK_ByCopy) {
93 const VarDecl *Var = dyn_cast<VarDecl>(Capture.getCapturedVar());
94 if (Var->hasInit()) {
95 const Stmt *Init = Var->getInit();
97 // Ignore single argument constructors, and trivial nodes.
98 while (true) {
99 auto NewInit = IgnoreTrivials(Init);
100 if (auto ConstructExpr = dyn_cast<CXXConstructExpr>(NewInit)) {
101 if (ConstructExpr->getNumArgs() == 1) {
102 NewInit = ConstructExpr->getArg(0);
105 if (Init == NewInit) {
106 break;
108 Init = NewInit;
111 if (isa<CXXThisExpr>(Init)) {
112 StrongRefToThisCaptured = true;
118 // Now we can go through and produce errors for any captured variables or this
119 // pointers.
120 for (const LambdaCapture &Capture : Lambda->captures()) {
121 if (Capture.capturesVariable()) {
122 const VarDecl *Var = dyn_cast<VarDecl>(Capture.getCapturedVar());
123 QualType Pointee = Var->getType()->getPointeeType();
124 if (!Pointee.isNull() && isClassRefCounted(Pointee) &&
125 !IsKnownLive(Var)) {
126 emitDiagnostics(Capture.getLocation(), Var->getName(), Pointee);
127 return;
131 // The situation with captures of `this` is more complex. All captures of
132 // `this` look the same-ish (they are LCK_This). We want to complain about
133 // captures of `this` where `this` is a refcounted type, and the capture is
134 // actually used in the body of the lambda (if the capture isn't used, then
135 // we don't care, because it's only being captured in order to give access
136 // to private methods).
138 // In addition, we don't complain about this, even if it is used, if it was
139 // captured implicitly when the LambdaCaptureDefault was LCD_ByRef, as that
140 // expresses the intent that the lambda won't leave the enclosing scope.
141 bool ImplicitByRefDefaultedCapture =
142 Capture.isImplicit() && Lambda->getLambdaCaptureDefault() == LCD_ByRef;
143 if (Capture.capturesThis() && !ImplicitByRefDefaultedCapture &&
144 !StrongRefToThisCaptured) {
145 ThisVisitor V(*this);
146 bool NotAborted = V.TraverseDecl(
147 const_cast<CXXMethodDecl *>(Lambda->getLambdaCallOperator()));
148 if (!NotAborted) {
149 return;
155 bool RefCountedInsideLambdaChecker::ThisVisitor::VisitCXXThisExpr(
156 CXXThisExpr *This) {
157 QualType Pointee = This->getType()->getPointeeType();
158 if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
159 Checker.emitDiagnostics(This->getBeginLoc(), "this", Pointee);
160 return false;
163 return true;