brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.6 KiB · c0727ae Raw
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