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