xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //=== LLVMConventionsChecker.cpp - Check LLVM codebase conventions ---*- C++ -*-
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // This defines LLVMConventionsChecker, a bunch of small little checks
10*0b57cec5SDimitry Andric // for checking specific coding conventions in the LLVM/Clang codebase.
11*0b57cec5SDimitry Andric //
12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
13*0b57cec5SDimitry Andric 
14*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15*0b57cec5SDimitry Andric #include "clang/AST/DeclTemplate.h"
16*0b57cec5SDimitry Andric #include "clang/AST/StmtVisitor.h"
17*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
19*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
20*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
21*0b57cec5SDimitry Andric 
22*0b57cec5SDimitry Andric using namespace clang;
23*0b57cec5SDimitry Andric using namespace ento;
24*0b57cec5SDimitry Andric 
25*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
26*0b57cec5SDimitry Andric // Generic type checking routines.
27*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
28*0b57cec5SDimitry Andric 
29*0b57cec5SDimitry Andric static bool IsLLVMStringRef(QualType T) {
30*0b57cec5SDimitry Andric   const RecordType *RT = T->getAs<RecordType>();
31*0b57cec5SDimitry Andric   if (!RT)
32*0b57cec5SDimitry Andric     return false;
33*0b57cec5SDimitry Andric 
34*0b57cec5SDimitry Andric   return StringRef(QualType(RT, 0).getAsString()) == "class StringRef";
35*0b57cec5SDimitry Andric }
36*0b57cec5SDimitry Andric 
37*0b57cec5SDimitry Andric /// Check whether the declaration is semantically inside the top-level
38*0b57cec5SDimitry Andric /// namespace named by ns.
39*0b57cec5SDimitry Andric static bool InNamespace(const Decl *D, StringRef NS) {
40*0b57cec5SDimitry Andric   const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext());
41*0b57cec5SDimitry Andric   if (!ND)
42*0b57cec5SDimitry Andric     return false;
43*0b57cec5SDimitry Andric   const IdentifierInfo *II = ND->getIdentifier();
44*0b57cec5SDimitry Andric   if (!II || !II->getName().equals(NS))
45*0b57cec5SDimitry Andric     return false;
46*0b57cec5SDimitry Andric   return isa<TranslationUnitDecl>(ND->getDeclContext());
47*0b57cec5SDimitry Andric }
48*0b57cec5SDimitry Andric 
49*0b57cec5SDimitry Andric static bool IsStdString(QualType T) {
50*0b57cec5SDimitry Andric   if (const ElaboratedType *QT = T->getAs<ElaboratedType>())
51*0b57cec5SDimitry Andric     T = QT->getNamedType();
52*0b57cec5SDimitry Andric 
53*0b57cec5SDimitry Andric   const TypedefType *TT = T->getAs<TypedefType>();
54*0b57cec5SDimitry Andric   if (!TT)
55*0b57cec5SDimitry Andric     return false;
56*0b57cec5SDimitry Andric 
57*0b57cec5SDimitry Andric   const TypedefNameDecl *TD = TT->getDecl();
58*0b57cec5SDimitry Andric 
59*0b57cec5SDimitry Andric   if (!TD->isInStdNamespace())
60*0b57cec5SDimitry Andric     return false;
61*0b57cec5SDimitry Andric 
62*0b57cec5SDimitry Andric   return TD->getName() == "string";
63*0b57cec5SDimitry Andric }
64*0b57cec5SDimitry Andric 
65*0b57cec5SDimitry Andric static bool IsClangType(const RecordDecl *RD) {
66*0b57cec5SDimitry Andric   return RD->getName() == "Type" && InNamespace(RD, "clang");
67*0b57cec5SDimitry Andric }
68*0b57cec5SDimitry Andric 
69*0b57cec5SDimitry Andric static bool IsClangDecl(const RecordDecl *RD) {
70*0b57cec5SDimitry Andric   return RD->getName() == "Decl" && InNamespace(RD, "clang");
71*0b57cec5SDimitry Andric }
72*0b57cec5SDimitry Andric 
73*0b57cec5SDimitry Andric static bool IsClangStmt(const RecordDecl *RD) {
74*0b57cec5SDimitry Andric   return RD->getName() == "Stmt" && InNamespace(RD, "clang");
75*0b57cec5SDimitry Andric }
76*0b57cec5SDimitry Andric 
77*0b57cec5SDimitry Andric static bool IsClangAttr(const RecordDecl *RD) {
78*0b57cec5SDimitry Andric   return RD->getName() == "Attr" && InNamespace(RD, "clang");
79*0b57cec5SDimitry Andric }
80*0b57cec5SDimitry Andric 
81*0b57cec5SDimitry Andric static bool IsStdVector(QualType T) {
82*0b57cec5SDimitry Andric   const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>();
83*0b57cec5SDimitry Andric   if (!TS)
84*0b57cec5SDimitry Andric     return false;
85*0b57cec5SDimitry Andric 
86*0b57cec5SDimitry Andric   TemplateName TM = TS->getTemplateName();
87*0b57cec5SDimitry Andric   TemplateDecl *TD = TM.getAsTemplateDecl();
88*0b57cec5SDimitry Andric 
89*0b57cec5SDimitry Andric   if (!TD || !InNamespace(TD, "std"))
90*0b57cec5SDimitry Andric     return false;
91*0b57cec5SDimitry Andric 
92*0b57cec5SDimitry Andric   return TD->getName() == "vector";
93*0b57cec5SDimitry Andric }
94*0b57cec5SDimitry Andric 
95*0b57cec5SDimitry Andric static bool IsSmallVector(QualType T) {
96*0b57cec5SDimitry Andric   const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>();
97*0b57cec5SDimitry Andric   if (!TS)
98*0b57cec5SDimitry Andric     return false;
99*0b57cec5SDimitry Andric 
100*0b57cec5SDimitry Andric   TemplateName TM = TS->getTemplateName();
101*0b57cec5SDimitry Andric   TemplateDecl *TD = TM.getAsTemplateDecl();
102*0b57cec5SDimitry Andric 
103*0b57cec5SDimitry Andric   if (!TD || !InNamespace(TD, "llvm"))
104*0b57cec5SDimitry Andric     return false;
105*0b57cec5SDimitry Andric 
106*0b57cec5SDimitry Andric   return TD->getName() == "SmallVector";
107*0b57cec5SDimitry Andric }
108*0b57cec5SDimitry Andric 
109*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
110*0b57cec5SDimitry Andric // CHECK: a StringRef should not be bound to a temporary std::string whose
111*0b57cec5SDimitry Andric // lifetime is shorter than the StringRef's.
112*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
113*0b57cec5SDimitry Andric 
114*0b57cec5SDimitry Andric namespace {
115*0b57cec5SDimitry Andric class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> {
116*0b57cec5SDimitry Andric   const Decl *DeclWithIssue;
117*0b57cec5SDimitry Andric   BugReporter &BR;
118*0b57cec5SDimitry Andric   const CheckerBase *Checker;
119*0b57cec5SDimitry Andric 
120*0b57cec5SDimitry Andric public:
121*0b57cec5SDimitry Andric   StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br,
122*0b57cec5SDimitry Andric                           const CheckerBase *checker)
123*0b57cec5SDimitry Andric       : DeclWithIssue(declWithIssue), BR(br), Checker(checker) {}
124*0b57cec5SDimitry Andric   void VisitChildren(Stmt *S) {
125*0b57cec5SDimitry Andric     for (Stmt *Child : S->children())
126*0b57cec5SDimitry Andric       if (Child)
127*0b57cec5SDimitry Andric         Visit(Child);
128*0b57cec5SDimitry Andric   }
129*0b57cec5SDimitry Andric   void VisitStmt(Stmt *S) { VisitChildren(S); }
130*0b57cec5SDimitry Andric   void VisitDeclStmt(DeclStmt *DS);
131*0b57cec5SDimitry Andric private:
132*0b57cec5SDimitry Andric   void VisitVarDecl(VarDecl *VD);
133*0b57cec5SDimitry Andric };
134*0b57cec5SDimitry Andric } // end anonymous namespace
135*0b57cec5SDimitry Andric 
136*0b57cec5SDimitry Andric static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR,
137*0b57cec5SDimitry Andric                                             const CheckerBase *Checker) {
138*0b57cec5SDimitry Andric   StringRefCheckerVisitor walker(D, BR, Checker);
139*0b57cec5SDimitry Andric   walker.Visit(D->getBody());
140*0b57cec5SDimitry Andric }
141*0b57cec5SDimitry Andric 
142*0b57cec5SDimitry Andric void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) {
143*0b57cec5SDimitry Andric   VisitChildren(S);
144*0b57cec5SDimitry Andric 
145*0b57cec5SDimitry Andric   for (auto *I : S->decls())
146*0b57cec5SDimitry Andric     if (VarDecl *VD = dyn_cast<VarDecl>(I))
147*0b57cec5SDimitry Andric       VisitVarDecl(VD);
148*0b57cec5SDimitry Andric }
149*0b57cec5SDimitry Andric 
150*0b57cec5SDimitry Andric void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) {
151*0b57cec5SDimitry Andric   Expr *Init = VD->getInit();
152*0b57cec5SDimitry Andric   if (!Init)
153*0b57cec5SDimitry Andric     return;
154*0b57cec5SDimitry Andric 
155*0b57cec5SDimitry Andric   // Pattern match for:
156*0b57cec5SDimitry Andric   // StringRef x = call() (where call returns std::string)
157*0b57cec5SDimitry Andric   if (!IsLLVMStringRef(VD->getType()))
158*0b57cec5SDimitry Andric     return;
159*0b57cec5SDimitry Andric   ExprWithCleanups *Ex1 = dyn_cast<ExprWithCleanups>(Init);
160*0b57cec5SDimitry Andric   if (!Ex1)
161*0b57cec5SDimitry Andric     return;
162*0b57cec5SDimitry Andric   CXXConstructExpr *Ex2 = dyn_cast<CXXConstructExpr>(Ex1->getSubExpr());
163*0b57cec5SDimitry Andric   if (!Ex2 || Ex2->getNumArgs() != 1)
164*0b57cec5SDimitry Andric     return;
165*0b57cec5SDimitry Andric   ImplicitCastExpr *Ex3 = dyn_cast<ImplicitCastExpr>(Ex2->getArg(0));
166*0b57cec5SDimitry Andric   if (!Ex3)
167*0b57cec5SDimitry Andric     return;
168*0b57cec5SDimitry Andric   CXXConstructExpr *Ex4 = dyn_cast<CXXConstructExpr>(Ex3->getSubExpr());
169*0b57cec5SDimitry Andric   if (!Ex4 || Ex4->getNumArgs() != 1)
170*0b57cec5SDimitry Andric     return;
171*0b57cec5SDimitry Andric   ImplicitCastExpr *Ex5 = dyn_cast<ImplicitCastExpr>(Ex4->getArg(0));
172*0b57cec5SDimitry Andric   if (!Ex5)
173*0b57cec5SDimitry Andric     return;
174*0b57cec5SDimitry Andric   CXXBindTemporaryExpr *Ex6 = dyn_cast<CXXBindTemporaryExpr>(Ex5->getSubExpr());
175*0b57cec5SDimitry Andric   if (!Ex6 || !IsStdString(Ex6->getType()))
176*0b57cec5SDimitry Andric     return;
177*0b57cec5SDimitry Andric 
178*0b57cec5SDimitry Andric   // Okay, badness!  Report an error.
179*0b57cec5SDimitry Andric   const char *desc = "StringRef should not be bound to temporary "
180*0b57cec5SDimitry Andric                      "std::string that it outlives";
181*0b57cec5SDimitry Andric   PathDiagnosticLocation VDLoc =
182*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(VD, BR.getSourceManager());
183*0b57cec5SDimitry Andric   BR.EmitBasicReport(DeclWithIssue, Checker, desc, "LLVM Conventions", desc,
184*0b57cec5SDimitry Andric                      VDLoc, Init->getSourceRange());
185*0b57cec5SDimitry Andric }
186*0b57cec5SDimitry Andric 
187*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
188*0b57cec5SDimitry Andric // CHECK: Clang AST nodes should not have fields that can allocate
189*0b57cec5SDimitry Andric //   memory.
190*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
191*0b57cec5SDimitry Andric 
192*0b57cec5SDimitry Andric static bool AllocatesMemory(QualType T) {
193*0b57cec5SDimitry Andric   return IsStdVector(T) || IsStdString(T) || IsSmallVector(T);
194*0b57cec5SDimitry Andric }
195*0b57cec5SDimitry Andric 
196*0b57cec5SDimitry Andric // This type checking could be sped up via dynamic programming.
197*0b57cec5SDimitry Andric static bool IsPartOfAST(const CXXRecordDecl *R) {
198*0b57cec5SDimitry Andric   if (IsClangStmt(R) || IsClangType(R) || IsClangDecl(R) || IsClangAttr(R))
199*0b57cec5SDimitry Andric     return true;
200*0b57cec5SDimitry Andric 
201*0b57cec5SDimitry Andric   for (const auto &BS : R->bases()) {
202*0b57cec5SDimitry Andric     QualType T = BS.getType();
203*0b57cec5SDimitry Andric     if (const RecordType *baseT = T->getAs<RecordType>()) {
204*0b57cec5SDimitry Andric       CXXRecordDecl *baseD = cast<CXXRecordDecl>(baseT->getDecl());
205*0b57cec5SDimitry Andric       if (IsPartOfAST(baseD))
206*0b57cec5SDimitry Andric         return true;
207*0b57cec5SDimitry Andric     }
208*0b57cec5SDimitry Andric   }
209*0b57cec5SDimitry Andric 
210*0b57cec5SDimitry Andric   return false;
211*0b57cec5SDimitry Andric }
212*0b57cec5SDimitry Andric 
213*0b57cec5SDimitry Andric namespace {
214*0b57cec5SDimitry Andric class ASTFieldVisitor {
215*0b57cec5SDimitry Andric   SmallVector<FieldDecl*, 10> FieldChain;
216*0b57cec5SDimitry Andric   const CXXRecordDecl *Root;
217*0b57cec5SDimitry Andric   BugReporter &BR;
218*0b57cec5SDimitry Andric   const CheckerBase *Checker;
219*0b57cec5SDimitry Andric 
220*0b57cec5SDimitry Andric public:
221*0b57cec5SDimitry Andric   ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br,
222*0b57cec5SDimitry Andric                   const CheckerBase *checker)
223*0b57cec5SDimitry Andric       : Root(root), BR(br), Checker(checker) {}
224*0b57cec5SDimitry Andric 
225*0b57cec5SDimitry Andric   void Visit(FieldDecl *D);
226*0b57cec5SDimitry Andric   void ReportError(QualType T);
227*0b57cec5SDimitry Andric };
228*0b57cec5SDimitry Andric } // end anonymous namespace
229*0b57cec5SDimitry Andric 
230*0b57cec5SDimitry Andric static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR,
231*0b57cec5SDimitry Andric                            const CheckerBase *Checker) {
232*0b57cec5SDimitry Andric   if (!IsPartOfAST(R))
233*0b57cec5SDimitry Andric     return;
234*0b57cec5SDimitry Andric 
235*0b57cec5SDimitry Andric   for (auto *I : R->fields()) {
236*0b57cec5SDimitry Andric     ASTFieldVisitor walker(R, BR, Checker);
237*0b57cec5SDimitry Andric     walker.Visit(I);
238*0b57cec5SDimitry Andric   }
239*0b57cec5SDimitry Andric }
240*0b57cec5SDimitry Andric 
241*0b57cec5SDimitry Andric void ASTFieldVisitor::Visit(FieldDecl *D) {
242*0b57cec5SDimitry Andric   FieldChain.push_back(D);
243*0b57cec5SDimitry Andric 
244*0b57cec5SDimitry Andric   QualType T = D->getType();
245*0b57cec5SDimitry Andric 
246*0b57cec5SDimitry Andric   if (AllocatesMemory(T))
247*0b57cec5SDimitry Andric     ReportError(T);
248*0b57cec5SDimitry Andric 
249*0b57cec5SDimitry Andric   if (const RecordType *RT = T->getAs<RecordType>()) {
250*0b57cec5SDimitry Andric     const RecordDecl *RD = RT->getDecl()->getDefinition();
251*0b57cec5SDimitry Andric     for (auto *I : RD->fields())
252*0b57cec5SDimitry Andric       Visit(I);
253*0b57cec5SDimitry Andric   }
254*0b57cec5SDimitry Andric 
255*0b57cec5SDimitry Andric   FieldChain.pop_back();
256*0b57cec5SDimitry Andric }
257*0b57cec5SDimitry Andric 
258*0b57cec5SDimitry Andric void ASTFieldVisitor::ReportError(QualType T) {
259*0b57cec5SDimitry Andric   SmallString<1024> buf;
260*0b57cec5SDimitry Andric   llvm::raw_svector_ostream os(buf);
261*0b57cec5SDimitry Andric 
262*0b57cec5SDimitry Andric   os << "AST class '" << Root->getName() << "' has a field '"
263*0b57cec5SDimitry Andric      << FieldChain.front()->getName() << "' that allocates heap memory";
264*0b57cec5SDimitry Andric   if (FieldChain.size() > 1) {
265*0b57cec5SDimitry Andric     os << " via the following chain: ";
266*0b57cec5SDimitry Andric     bool isFirst = true;
267*0b57cec5SDimitry Andric     for (SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(),
268*0b57cec5SDimitry Andric          E=FieldChain.end(); I!=E; ++I) {
269*0b57cec5SDimitry Andric       if (!isFirst)
270*0b57cec5SDimitry Andric         os << '.';
271*0b57cec5SDimitry Andric       else
272*0b57cec5SDimitry Andric         isFirst = false;
273*0b57cec5SDimitry Andric       os << (*I)->getName();
274*0b57cec5SDimitry Andric     }
275*0b57cec5SDimitry Andric   }
276*0b57cec5SDimitry Andric   os << " (type " << FieldChain.back()->getType().getAsString() << ")";
277*0b57cec5SDimitry Andric 
278*0b57cec5SDimitry Andric   // Note that this will fire for every translation unit that uses this
279*0b57cec5SDimitry Andric   // class.  This is suboptimal, but at least scan-build will merge
280*0b57cec5SDimitry Andric   // duplicate HTML reports.  In the future we need a unified way of merging
281*0b57cec5SDimitry Andric   // duplicate reports across translation units.  For C++ classes we cannot
282*0b57cec5SDimitry Andric   // just report warnings when we see an out-of-line method definition for a
283*0b57cec5SDimitry Andric   // class, as that heuristic doesn't always work (the complete definition of
284*0b57cec5SDimitry Andric   // the class may be in the header file, for example).
285*0b57cec5SDimitry Andric   PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(
286*0b57cec5SDimitry Andric                                FieldChain.front(), BR.getSourceManager());
287*0b57cec5SDimitry Andric   BR.EmitBasicReport(Root, Checker, "AST node allocates heap memory",
288*0b57cec5SDimitry Andric                      "LLVM Conventions", os.str(), L);
289*0b57cec5SDimitry Andric }
290*0b57cec5SDimitry Andric 
291*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
292*0b57cec5SDimitry Andric // LLVMConventionsChecker
293*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
294*0b57cec5SDimitry Andric 
295*0b57cec5SDimitry Andric namespace {
296*0b57cec5SDimitry Andric class LLVMConventionsChecker : public Checker<
297*0b57cec5SDimitry Andric                                                 check::ASTDecl<CXXRecordDecl>,
298*0b57cec5SDimitry Andric                                                 check::ASTCodeBody > {
299*0b57cec5SDimitry Andric public:
300*0b57cec5SDimitry Andric   void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr,
301*0b57cec5SDimitry Andric                     BugReporter &BR) const {
302*0b57cec5SDimitry Andric     if (R->isCompleteDefinition())
303*0b57cec5SDimitry Andric       CheckASTMemory(R, BR, this);
304*0b57cec5SDimitry Andric   }
305*0b57cec5SDimitry Andric 
306*0b57cec5SDimitry Andric   void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
307*0b57cec5SDimitry Andric                         BugReporter &BR) const {
308*0b57cec5SDimitry Andric     CheckStringRefAssignedTemporary(D, BR, this);
309*0b57cec5SDimitry Andric   }
310*0b57cec5SDimitry Andric };
311*0b57cec5SDimitry Andric }
312*0b57cec5SDimitry Andric 
313*0b57cec5SDimitry Andric void ento::registerLLVMConventionsChecker(CheckerManager &mgr) {
314*0b57cec5SDimitry Andric   mgr.registerChecker<LLVMConventionsChecker>();
315*0b57cec5SDimitry Andric }
316*0b57cec5SDimitry Andric 
317*0b57cec5SDimitry Andric bool ento::shouldRegisterLLVMConventionsChecker(const LangOptions &LO) {
318*0b57cec5SDimitry Andric   return true;
319*0b57cec5SDimitry Andric }
320