1//=======- NoDeleteChecker.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/Decl.h"
13#include "clang/AST/DeclCXX.h"
14#include "clang/AST/DynamicRecursiveASTVisitor.h"
15#include "clang/AST/QualTypeNames.h"
16#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
17#include "clang/Basic/SourceLocation.h"
18#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21#include "clang/StaticAnalyzer/Core/Checker.h"
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27
28class NoDeleteChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
29 BugType Bug;
30 mutable BugReporter *BR = nullptr;
31 mutable TrivialFunctionAnalysis TFA;
32
33public:
34 NoDeleteChecker()
35 : Bug(this,
36 "Incorrect [[clang::annotate_type(\"webkit.nodelete\")]] "
37 "annotation",
38 "WebKit coding guidelines") {}
39
40 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
41 BugReporter &BRArg) const {
42 BR = &BRArg;
43
44 // The calls to checkAST* from AnalysisConsumer don't
45 // visit template instantiations or lambda classes. We
46 // want to visit those, so we make our own visitor.
47 struct LocalVisitor final : public ConstDynamicRecursiveASTVisitor {
48 const NoDeleteChecker *Checker;
49 Decl *DeclWithIssue{nullptr};
50
51 explicit LocalVisitor(const NoDeleteChecker *Checker) : Checker(Checker) {
52 assert(Checker);
53 ShouldVisitTemplateInstantiations = true;
54 ShouldWalkTypesOfTypeLocs = true;
55 ShouldVisitImplicitCode = false;
56 ShouldVisitLambdaBody = true;
57 }
58
59 bool VisitFunctionDecl(const FunctionDecl *FD) override {
60 Checker->visitFunctionDecl(FD);
61 return true;
62 }
63 };
64
65 LocalVisitor visitor(this);
66 visitor.TraverseDecl(D: const_cast<TranslationUnitDecl *>(TUD));
67 }
68
69 static bool hasNoDeleteAnnotation(const FunctionDecl *FD) {
70 if (llvm::any_of(Range: FD->redecls(), P: isNoDeleteFunction))
71 return true;
72
73 const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD);
74 if (!MD || !MD->isVirtual())
75 return false;
76
77 auto Overriders = llvm::to_vector(Range: MD->overridden_methods());
78 while (!Overriders.empty()) {
79 const auto *Fn = Overriders.pop_back_val();
80 llvm::append_range(C&: Overriders, R: Fn->overridden_methods());
81 if (isNoDeleteFunction(F: Fn))
82 return true;
83 }
84
85 return false;
86 }
87
88 void visitFunctionDecl(const FunctionDecl *FD) const {
89 if (!FD->doesThisDeclarationHaveABody() || FD->isDependentContext())
90 return;
91
92 if (!hasNoDeleteAnnotation(FD))
93 return;
94
95 auto Body = FD->getBody();
96 if (!Body)
97 return;
98
99 NamedDecl *ParamDecl = nullptr;
100 for (auto *D : FD->parameters()) {
101 if (!TFA.hasTrivialDtor(VD: D)) {
102 ParamDecl = D;
103 break;
104 }
105 }
106 const Stmt *OffendingStmt = nullptr;
107 if (!ParamDecl && TFA.isTrivial(S: Body, OffendingStmt: &OffendingStmt))
108 return;
109
110 SmallString<100> Buf;
111 llvm::raw_svector_ostream Os(Buf);
112
113 Os << "A function ";
114 printQuotedName(Os, D: FD);
115 Os << " has [[clang::annotate_type(\"webkit.nodelete\")]] but it contains ";
116 SourceLocation SrcLocToReport;
117 SourceRange Range;
118 if (ParamDecl) {
119 Os << "a parameter ";
120 printQuotedName(Os, D: ParamDecl);
121 Os << " which could destruct an object.";
122 SrcLocToReport = FD->getBeginLoc();
123 Range = ParamDecl->getSourceRange();
124 } else {
125 Os << "code that could destruct an object.";
126 SrcLocToReport = OffendingStmt->getBeginLoc();
127 Range = OffendingStmt->getSourceRange();
128 }
129
130 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
131 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
132 Report->addRange(R: Range);
133 Report->setDeclWithIssue(FD);
134 BR->emitReport(R: std::move(Report));
135 }
136};
137
138} // namespace
139
140void ento::registerNoDeleteChecker(CheckerManager &Mgr) {
141 Mgr.registerChecker<NoDeleteChecker>();
142}
143
144bool ento::shouldRegisterNoDeleteChecker(const CheckerManager &) {
145 return true;
146}
147