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