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