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