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