| //=======- RawPtrRefLambdaCapturesChecker.cpp --------------------*- C++ -*-==// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ASTUtils.h" |
| #include "DiagOutputUtils.h" |
| #include "PtrTypesSemantics.h" |
| #include "RawPtrRefSafetyModel.h" |
| #include "clang/AST/DynamicRecursiveASTVisitor.h" |
| #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
| #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
| #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
| #include "clang/StaticAnalyzer/Core/Checker.h" |
| #include <optional> |
| |
| using namespace clang; |
| using namespace ento; |
| |
| namespace { |
| class RawPtrRefLambdaCapturesChecker |
| : public Checker<check::ASTDecl<TranslationUnitDecl>> { |
| private: |
| BugType Bug; |
| mutable BugReporter *BR = nullptr; |
| TrivialFunctionAnalysis TFA; |
| |
| protected: |
| const std::unique_ptr<PtrRefSafetyModel> Model; |
| |
| public: |
| RawPtrRefLambdaCapturesChecker(const char *description, |
| std::unique_ptr<PtrRefSafetyModel> Model) |
| : Bug(this, description, "WebKit coding guidelines"), |
| Model(std::move(Model)) {} |
| |
| std::optional<bool> isUnsafePtr(QualType QT) const { |
| return isUnsafePtrForStorage(*Model, QT); |
| } |
| bool isPtrType(const std::string &Name) const { |
| return Model->isPtrType(Name); |
| } |
| |
| void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, |
| BugReporter &BRArg) const { |
| BR = &BRArg; |
| |
| // The calls to checkAST* from AnalysisConsumer don't |
| // visit template instantiations or lambda classes. We |
| // want to visit those, so we make our own RecursiveASTVisitor. |
| struct LocalVisitor : DynamicRecursiveASTVisitor { |
| const RawPtrRefLambdaCapturesChecker *Checker; |
| llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore; |
| llvm::DenseSet<const LambdaExpr *> LambdasToIgnore; |
| llvm::DenseSet<const ValueDecl *> ProtectedThisDecls; |
| llvm::DenseSet<const CallExpr *> CallToIgnore; |
| llvm::DenseSet<const CXXConstructExpr *> ConstructToIgnore; |
| llvm::DenseMap<const VarDecl *, SmallVector<const LambdaExpr *>> |
| LambdaOwnerMap; |
| |
| QualType ClsType; |
| |
| explicit LocalVisitor(const RawPtrRefLambdaCapturesChecker *Checker) |
| : Checker(Checker) { |
| assert(Checker); |
| ShouldVisitTemplateInstantiations = true; |
| ShouldVisitImplicitCode = false; |
| } |
| |
| bool TraverseCXXConstructorDecl(CXXConstructorDecl *Ctor) override { |
| llvm::SaveAndRestore SavedDecl(ClsType); |
| ClsType = Ctor->getThisType(); |
| return DynamicRecursiveASTVisitor::TraverseCXXConstructorDecl(Ctor); |
| } |
| |
| bool TraverseCXXDestructorDecl(CXXDestructorDecl *Dtor) override { |
| llvm::SaveAndRestore SavedDecl(ClsType); |
| ClsType = Dtor->getThisType(); |
| return DynamicRecursiveASTVisitor::TraverseCXXDestructorDecl(Dtor); |
| } |
| |
| bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD) override { |
| llvm::SaveAndRestore SavedDecl(ClsType); |
| if (CXXMD->isInstance()) |
| ClsType = CXXMD->getThisType(); |
| return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD); |
| } |
| |
| bool TraverseObjCMethodDecl(ObjCMethodDecl *OCMD) override { |
| llvm::SaveAndRestore SavedDecl(ClsType); |
| if (OCMD && OCMD->isInstanceMethod()) { |
| if (auto *ImplParamDecl = OCMD->getSelfDecl()) |
| ClsType = ImplParamDecl->getType(); |
| } |
| return DynamicRecursiveASTVisitor::TraverseObjCMethodDecl(OCMD); |
| } |
| |
| bool VisitTypedefDecl(TypedefDecl *TD) override { |
| if (auto *RTC = Checker->Model->retainTypeChecker()) |
| RTC->visitTypedef(TD); |
| return true; |
| } |
| |
| bool shouldCheckThis() { |
| auto result = |
| !ClsType.isNull() ? Checker->isUnsafePtr(ClsType) : std::nullopt; |
| return result && *result; |
| } |
| |
| bool VisitLambdaExpr(LambdaExpr *L) override { |
| if (LambdasToIgnore.contains(L)) |
| return true; |
| Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L), |
| ClsType); |
| return true; |
| } |
| |
| bool VisitVarDecl(VarDecl *VD) override { |
| auto *Init = VD->getInit(); |
| if (!Init) |
| return true; |
| if (auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts())) { |
| LambdasToIgnore.insert(L); // Evaluate lambdas in VisitDeclRefExpr. |
| return true; |
| } |
| if (!VD->hasLocalStorage()) |
| return true; |
| if (auto *E = dyn_cast<ExprWithCleanups>(Init)) |
| Init = E->getSubExpr(); |
| if (auto *E = dyn_cast<CXXBindTemporaryExpr>(Init)) |
| Init = E->getSubExpr(); |
| if (auto *CE = dyn_cast<CallExpr>(Init)) { |
| if (auto *Callee = CE->getDirectCallee()) { |
| auto FnName = safeGetName(Callee); |
| unsigned ArgCnt = CE->getNumArgs(); |
| if (FnName == "makeScopeExit" && ArgCnt == 1) { |
| auto *Arg = CE->getArg(0); |
| if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg)) |
| Arg = E->getSubExpr(); |
| if (auto *L = dyn_cast<LambdaExpr>(Arg)) |
| addLambdaOwner(VD, CE, L); |
| } else if (FnName == "makeVisitor") { |
| for (unsigned ArgIndex = 0; ArgIndex < ArgCnt; ++ArgIndex) { |
| auto *Arg = CE->getArg(ArgIndex); |
| if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg)) |
| Arg = E->getSubExpr(); |
| if (auto *L = dyn_cast<LambdaExpr>(Arg)) |
| addLambdaOwner(VD, CE, L); |
| } |
| } |
| } |
| } else if (auto *CE = dyn_cast<CXXConstructExpr>(Init)) { |
| if (auto *Ctor = CE->getConstructor()) { |
| if (auto *Cls = Ctor->getParent()) { |
| auto FnName = safeGetName(Cls); |
| unsigned ArgCnt = CE->getNumArgs(); |
| if (FnName == "ScopeExit" && ArgCnt == 1) { |
| auto *Arg = CE->getArg(0); |
| if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg)) |
| Arg = E->getSubExpr(); |
| if (auto *L = dyn_cast<LambdaExpr>(Arg)) |
| addLambdaOwner(VD, CE, L); |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| void addLambdaOwner(VarDecl *VD, CallExpr *CE, LambdaExpr *L) { |
| auto result = LambdaOwnerMap.insert( |
| std::make_pair(VD, SmallVector<const LambdaExpr *>{L})); |
| if (!result.second) |
| result.first->second.push_back(L); |
| CallToIgnore.insert(CE); |
| LambdasToIgnore.insert(L); |
| } |
| |
| void addLambdaOwner(VarDecl *VD, CXXConstructExpr *CE, LambdaExpr *L) { |
| auto result = LambdaOwnerMap.insert( |
| std::make_pair(VD, SmallVector<const LambdaExpr *>{L})); |
| if (!result.second) |
| result.first->second.push_back(L); |
| ConstructToIgnore.insert(CE); |
| LambdasToIgnore.insert(L); |
| } |
| |
| bool VisitDeclRefExpr(DeclRefExpr *DRE) override { |
| if (DeclRefExprsToIgnore.contains(DRE)) |
| return true; |
| auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl()); |
| if (!VD) |
| return true; |
| if (auto It = LambdaOwnerMap.find(VD); It != LambdaOwnerMap.end()) { |
| for (auto *L : It->second) { |
| Checker->visitLambdaExpr( |
| L, shouldCheckThis() && !hasProtectedThis(L), ClsType); |
| } |
| return true; |
| } |
| auto *Init = VD->getInit(); |
| if (!Init) |
| return true; |
| auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); |
| if (!L) |
| return true; |
| LambdasToIgnore.insert(L); |
| Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L), |
| ClsType); |
| return true; |
| } |
| |
| bool shouldTreatAllArgAsNoEscape(FunctionDecl *FDecl) { |
| std::string PreviousName = safeGetName(FDecl); |
| for (auto *Decl = FDecl->getParent(); Decl; Decl = Decl->getParent()) { |
| if (!isa<NamespaceDecl>(Decl) && !isa<CXXRecordDecl>(Decl)) |
| return false; |
| auto Name = safeGetName(Decl); |
| // WTF::switchOn(T, F... f) is a variadic template function and |
| // couldn't be annotated with NOESCAPE. We hard code it here to |
| // workaround that. |
| if (Name == "WTF" && PreviousName == "switchOn") |
| return true; |
| // Treat every argument of functions in std::ranges as noescape. |
| if (Name == "std" && PreviousName == "ranges") |
| return true; |
| PreviousName = Name; |
| } |
| return false; |
| } |
| |
| bool VisitCXXConstructExpr(CXXConstructExpr *CE) override { |
| if (ConstructToIgnore.contains(CE)) |
| return true; |
| if (auto *Callee = CE->getConstructor()) { |
| unsigned ArgIndex = 0; |
| for (auto *Param : Callee->parameters()) { |
| if (ArgIndex >= CE->getNumArgs()) |
| return true; |
| auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts(); |
| if (auto *L = findLambdaInArg(Arg)) { |
| LambdasToIgnore.insert(L); |
| if (!Param->hasAttr<NoEscapeAttr>()) |
| Checker->visitLambdaExpr( |
| L, shouldCheckThis() && !hasProtectedThis(L), ClsType); |
| } |
| ++ArgIndex; |
| } |
| } |
| return true; |
| } |
| |
| bool VisitCallExpr(CallExpr *CE) override { |
| if (CallToIgnore.contains(CE)) |
| return true; |
| checkCalleeLambda(CE); |
| if (auto *Callee = CE->getDirectCallee()) { |
| if (isVisitFunction(CE, Callee)) |
| return true; |
| checkParameters(CE, Callee); |
| } else if (auto *CalleeE = CE->getCallee()) { |
| if (auto *DRE = dyn_cast<DeclRefExpr>(CalleeE->IgnoreParenCasts())) { |
| if (auto *Callee = dyn_cast_or_null<FunctionDecl>(DRE->getDecl())) |
| checkParameters(CE, Callee); |
| } |
| } |
| return true; |
| } |
| |
| bool isVisitFunction(CallExpr *CallExpr, FunctionDecl *FnDecl) { |
| bool IsVisitFn = safeGetName(FnDecl) == "visit"; |
| if (!IsVisitFn) |
| return false; |
| bool ArgCnt = CallExpr->getNumArgs(); |
| if (!ArgCnt) |
| return false; |
| auto *Ns = FnDecl->getParent(); |
| if (!Ns) |
| return false; |
| auto NsName = safeGetName(Ns); |
| if (NsName != "WTF" && NsName != "std") |
| return false; |
| auto *Arg = CallExpr->getArg(0); |
| if (!Arg) |
| return false; |
| auto *DRE = dyn_cast<DeclRefExpr>(Arg->IgnoreParenCasts()); |
| if (!DRE) |
| return false; |
| auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); |
| if (!VD) |
| return false; |
| if (!LambdaOwnerMap.contains(VD)) |
| return false; |
| DeclRefExprsToIgnore.insert(DRE); |
| return true; |
| } |
| |
| void checkParameters(CallExpr *CE, FunctionDecl *Callee) { |
| unsigned ArgIndex = isa<CXXOperatorCallExpr>(CE); |
| bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(Callee); |
| for (auto *Param : Callee->parameters()) { |
| if (ArgIndex >= CE->getNumArgs()) |
| return; |
| auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts(); |
| if (auto *L = findLambdaInArg(Arg)) { |
| LambdasToIgnore.insert(L); |
| if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape) |
| Checker->visitLambdaExpr( |
| L, shouldCheckThis() && !hasProtectedThis(L), ClsType); |
| } |
| ++ArgIndex; |
| } |
| } |
| |
| LambdaExpr *findLambdaInArg(Expr *E) { |
| if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(E)) |
| return Lambda; |
| auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(E); |
| if (!TempExpr) |
| return nullptr; |
| E = TempExpr->getSubExpr()->IgnoreParenCasts(); |
| if (!E) |
| return nullptr; |
| if (auto *Lambda = dyn_cast<LambdaExpr>(E)) |
| return Lambda; |
| auto *CE = dyn_cast_or_null<CXXConstructExpr>(E); |
| if (!CE || !CE->getNumArgs()) |
| return nullptr; |
| auto *CtorArg = CE->getArg(0)->IgnoreParenCasts(); |
| if (!CtorArg) |
| return nullptr; |
| auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(CtorArg); |
| if (InnerCE && InnerCE->getNumArgs()) |
| CtorArg = InnerCE->getArg(0)->IgnoreParenCasts(); |
| auto updateIgnoreList = [&] { |
| ConstructToIgnore.insert(CE); |
| if (InnerCE) |
| ConstructToIgnore.insert(InnerCE); |
| }; |
| if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) { |
| updateIgnoreList(); |
| return Lambda; |
| } |
| if (auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(CtorArg)) { |
| E = TempExpr->getSubExpr()->IgnoreParenCasts(); |
| if (auto *Lambda = dyn_cast<LambdaExpr>(E)) { |
| updateIgnoreList(); |
| return Lambda; |
| } |
| } |
| auto *DRE = dyn_cast<DeclRefExpr>(CtorArg); |
| if (!DRE) |
| return nullptr; |
| auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl()); |
| if (!VD) |
| return nullptr; |
| auto *Init = VD->getInit(); |
| if (!Init) |
| return nullptr; |
| if (auto *Lambda = dyn_cast<LambdaExpr>(Init)) { |
| DeclRefExprsToIgnore.insert(DRE); |
| updateIgnoreList(); |
| return Lambda; |
| } |
| return nullptr; |
| } |
| |
| void checkCalleeLambda(CallExpr *CE) { |
| auto *Callee = CE->getCallee(); |
| if (!Callee) |
| return; |
| Callee = Callee->IgnoreParenCasts(); |
| if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Callee)) { |
| Callee = MTE->getSubExpr(); |
| if (!Callee) |
| return; |
| Callee = Callee->IgnoreParenCasts(); |
| } |
| if (auto *L = dyn_cast<LambdaExpr>(Callee)) { |
| LambdasToIgnore.insert(L); // Calling a lambda upon creation is safe. |
| return; |
| } |
| auto *DRE = dyn_cast<DeclRefExpr>(Callee->IgnoreParenCasts()); |
| if (!DRE) |
| return; |
| auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->getDecl()); |
| 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; |
| auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl()); |
| if (!VD) |
| return; |
| auto *Init = VD->getInit(); |
| if (!Init) |
| return; |
| auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); |
| if (!L) |
| return; |
| DeclRefExprsToIgnore.insert(ArgRef); |
| LambdasToIgnore.insert(L); |
| } |
| |
| bool hasProtectedThis(const LambdaExpr *L) { |
| for (const LambdaCapture &OtherCapture : L->captures()) { |
| if (!OtherCapture.capturesVariable()) |
| continue; |
| if (auto *ValueDecl = OtherCapture.getCapturedVar()) { |
| if (declProtectsThis(ValueDecl)) { |
| ProtectedThisDecls.insert(ValueDecl); |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool declProtectsThis(const ValueDecl *ValueDecl) const { |
| auto *VD = dyn_cast<VarDecl>(ValueDecl); |
| if (!VD) |
| return false; |
| auto *Init = VD->getInit(); |
| if (!Init) |
| return false; |
| const Expr *Arg = Init->IgnoreParenCasts(); |
| do { |
| if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Arg)) |
| Arg = BTE->getSubExpr()->IgnoreParenCasts(); |
| if (auto *CE = dyn_cast<CXXConstructExpr>(Arg)) { |
| auto *Ctor = CE->getConstructor(); |
| if (!Ctor) |
| return false; |
| auto clsName = safeGetName(Ctor->getParent()); |
| if (Checker->isPtrType(clsName) && CE->getNumArgs()) { |
| Arg = CE->getArg(0)->IgnoreParenCasts(); |
| continue; |
| } |
| if (auto *Type = ClsType.getTypePtrOrNull()) { |
| if (auto *CXXR = Type->getPointeeCXXRecordDecl()) { |
| if (CXXR == Ctor->getParent() && Ctor->isMoveConstructor() && |
| CE->getNumArgs() == 1) { |
| Arg = CE->getArg(0)->IgnoreParenCasts(); |
| continue; |
| } |
| } |
| } |
| return false; |
| } |
| if (auto *CE = dyn_cast<CallExpr>(Arg)) { |
| if (auto *Callee = CE->getDirectCallee()) { |
| if ((isStdOrWTFMove(Callee) || isCtorOfSafePtr(Callee)) && |
| CE->getNumArgs() == 1) { |
| Arg = CE->getArg(0)->IgnoreParenCasts(); |
| continue; |
| } |
| } |
| } |
| if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Arg)) { |
| auto OpCode = OpCE->getOperator(); |
| if (OpCode == OO_Star || OpCode == OO_Amp) { |
| auto *Callee = OpCE->getDirectCallee(); |
| if (!Callee) |
| return false; |
| auto clsName = safeGetName(Callee->getParent()); |
| if (!Checker->isPtrType(clsName) || !OpCE->getNumArgs()) |
| return false; |
| Arg = OpCE->getArg(0)->IgnoreParenCasts(); |
| continue; |
| } |
| } |
| if (auto *UO = dyn_cast<UnaryOperator>(Arg)) { |
| auto OpCode = UO->getOpcode(); |
| if (OpCode == UO_Deref || OpCode == UO_AddrOf) { |
| Arg = UO->getSubExpr()->IgnoreParenCasts(); |
| continue; |
| } |
| } |
| break; |
| } while (Arg); |
| if (auto *DRE = dyn_cast<DeclRefExpr>(Arg)) { |
| auto *Decl = DRE->getDecl(); |
| if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Decl)) { |
| auto kind = ImplicitParam->getParameterKind(); |
| return kind == ImplicitParamKind::ObjCSelf || |
| kind == ImplicitParamKind::CXXThis; |
| } |
| return ProtectedThisDecls.contains(Decl); |
| } |
| return isa<CXXThisExpr>(Arg); |
| } |
| }; |
| |
| LocalVisitor visitor(this); |
| if (auto *RTC = Model->retainTypeChecker()) |
| RTC->visitTranslationUnitDecl(TUD); |
| visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); |
| } |
| |
| void visitLambdaExpr(const LambdaExpr *L, bool shouldCheckThis, |
| const QualType T, |
| bool ignoreParamVarDecl = false) const { |
| if (TFA.isTrivial(L->getBody())) |
| return; |
| for (const LambdaCapture &C : L->captures()) { |
| if (C.capturesVariable()) { |
| ValueDecl *CapturedVar = C.getCapturedVar(); |
| if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar)) |
| continue; |
| if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(CapturedVar)) { |
| auto kind = ImplicitParam->getParameterKind(); |
| if ((kind == ImplicitParamKind::ObjCSelf || |
| kind == ImplicitParamKind::CXXThis) && |
| !shouldCheckThis) |
| continue; |
| } |
| QualType CapturedVarQualType = CapturedVar->getType(); |
| auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType()); |
| if (C.getCaptureKind() == LCK_ByCopy && |
| CapturedVarQualType->isReferenceType()) |
| continue; |
| if (IsUncountedPtr && *IsUncountedPtr) |
| reportBug(C, CapturedVar, CapturedVarQualType, L); |
| } else if (C.capturesThis() && shouldCheckThis) { |
| if (ignoreParamVarDecl) // this is always a parameter to this function. |
| continue; |
| reportBugOnThisPtr(C, T); |
| } |
| } |
| } |
| |
| void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar, |
| const QualType T, const LambdaExpr *L) const { |
| assert(CapturedVar); |
| |
| auto Location = Capture.getLocation(); |
| if (isa<ImplicitParamDecl>(CapturedVar) && !Location.isValid()) |
| Location = L->getBeginLoc(); |
| |
| SmallString<100> Buf; |
| llvm::raw_svector_ostream Os(Buf); |
| |
| if (Capture.isExplicit()) |
| Os << "Captured "; |
| else |
| Os << "Implicitly captured "; |
| Os << "variable "; |
| printQuotedQualifiedName(Os, CapturedVar); |
| |
| bool IsUnsafePtr = CapturedVar->getType() == T; |
| if (IsUnsafePtr) |
| Os << " is a "; |
| else |
| Os << " contains a "; |
| auto *CapturedType = T.getTypePtrOrNull(); |
| printPointer(Os, CapturedType); |
| |
| PathDiagnosticLocation BSLoc(Location, BR->getSourceManager()); |
| auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); |
| BR->emitReport(std::move(Report)); |
| } |
| |
| void reportBugOnThisPtr(const LambdaCapture &Capture, |
| const QualType T) const { |
| SmallString<100> Buf; |
| llvm::raw_svector_ostream Os(Buf); |
| |
| if (Capture.isExplicit()) { |
| Os << "Captured "; |
| } else { |
| Os << "Implicitly captured "; |
| } |
| |
| Os << "variable 'this' is a raw pointer to " << Model->typeName(); |
| if (auto *RD = T->getPointeeCXXRecordDecl()) { |
| Os << " "; |
| printQuotedQualifiedName(Os, RD); |
| } |
| |
| PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); |
| auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); |
| BR->emitReport(std::move(Report)); |
| } |
| |
| void printPointer(llvm::raw_svector_ostream &Os, const Type *T) const { |
| if (Model->retainTypeChecker()) { |
| // An OS object may be spelled as an id qualified by an OS_-prefixed |
| // protocol; print that protocol name. |
| if (auto *ObjCPtr = dyn_cast<ObjCObjectPointerType>(T)) { |
| for (ObjCProtocolDecl *P : ObjCPtr->quals()) { |
| if (const auto *II = P->getIdentifier()) { |
| auto Name = II->getName(); |
| if (Name.starts_with("OS_")) { |
| Os << Model->typeName() << " "; |
| printQuotedQualifiedName(Os, P); |
| return; |
| } |
| } |
| } |
| } |
| // Retain/OS types are frequently spelled through a typedef (e.g. |
| // CFXXXRef); print the typedef name rather than desugaring. |
| if (!isa<ObjCObjectPointerType>(T) && T->getAs<TypedefType>()) { |
| auto Typedef = T->getAs<TypedefType>(); |
| assert(Typedef); |
| Os << Model->typeName() << " "; |
| printQuotedQualifiedName(Os, Typedef->getDecl()); |
| return; |
| } |
| } |
| T = T->getUnqualifiedDesugaredType(); |
| bool IsPtr = isa<PointerType>(T) || isa<ObjCObjectPointerType>(T); |
| Os << (IsPtr ? "raw pointer" : "raw reference") << " to "; |
| Os << Model->typeName(); |
| |
| if (auto *RD = T->getPointeeType()->getAsRecordDecl()) { |
| Os << " "; |
| printQuotedQualifiedName(Os, RD); |
| } else if (auto *ObjCDecl = getObjCDeclFromObjCPtr(T)) { |
| Os << " "; |
| printQuotedQualifiedName(Os, ObjCDecl); |
| } |
| } |
| }; |
| |
| class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { |
| public: |
| UncountedLambdaCapturesChecker() |
| : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted variable", |
| makeRefPtrSafetyModel()) {} |
| }; |
| |
| class UncheckedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { |
| public: |
| UncheckedLambdaCapturesChecker() |
| : RawPtrRefLambdaCapturesChecker("Lambda capture of unchecked variable", |
| makeCheckedPtrSafetyModel()) {} |
| }; |
| |
| class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { |
| public: |
| UnretainedLambdaCapturesChecker() |
| : RawPtrRefLambdaCapturesChecker("Lambda capture of unretained " |
| "variables", |
| makeRetainPtrSafetyModel()) {} |
| }; |
| |
| } // namespace |
| |
| void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) { |
| Mgr.registerChecker<UncountedLambdaCapturesChecker>(); |
| } |
| |
| bool ento::shouldRegisterUncountedLambdaCapturesChecker( |
| const CheckerManager &mgr) { |
| return true; |
| } |
| |
| void ento::registerUncheckedLambdaCapturesChecker(CheckerManager &Mgr) { |
| Mgr.registerChecker<UncheckedLambdaCapturesChecker>(); |
| } |
| |
| bool ento::shouldRegisterUncheckedLambdaCapturesChecker( |
| const CheckerManager &mgr) { |
| return true; |
| } |
| |
| void ento::registerUnretainedLambdaCapturesChecker(CheckerManager &Mgr) { |
| Mgr.registerChecker<UnretainedLambdaCapturesChecker>(); |
| } |
| |
| bool ento::shouldRegisterUnretainedLambdaCapturesChecker( |
| const CheckerManager &mgr) { |
| return true; |
| } |