10b57cec5SDimitry Andric //=== LLVMConventionsChecker.cpp - Check LLVM codebase conventions ---*- C++ -*- 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This defines LLVMConventionsChecker, a bunch of small little checks 100b57cec5SDimitry Andric // for checking specific coding conventions in the LLVM/Clang codebase. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 150b57cec5SDimitry Andric #include "clang/AST/DeclTemplate.h" 160b57cec5SDimitry Andric #include "clang/AST/StmtVisitor.h" 170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 190b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 200b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 210b57cec5SDimitry Andric 220b57cec5SDimitry Andric using namespace clang; 230b57cec5SDimitry Andric using namespace ento; 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 260b57cec5SDimitry Andric // Generic type checking routines. 270b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric static bool IsLLVMStringRef(QualType T) { 300b57cec5SDimitry Andric const RecordType *RT = T->getAs<RecordType>(); 310b57cec5SDimitry Andric if (!RT) 320b57cec5SDimitry Andric return false; 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric return StringRef(QualType(RT, 0).getAsString()) == "class StringRef"; 350b57cec5SDimitry Andric } 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric /// Check whether the declaration is semantically inside the top-level 380b57cec5SDimitry Andric /// namespace named by ns. 390b57cec5SDimitry Andric static bool InNamespace(const Decl *D, StringRef NS) { 400b57cec5SDimitry Andric const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext()); 410b57cec5SDimitry Andric if (!ND) 420b57cec5SDimitry Andric return false; 430b57cec5SDimitry Andric const IdentifierInfo *II = ND->getIdentifier(); 440b57cec5SDimitry Andric if (!II || !II->getName().equals(NS)) 450b57cec5SDimitry Andric return false; 460b57cec5SDimitry Andric return isa<TranslationUnitDecl>(ND->getDeclContext()); 470b57cec5SDimitry Andric } 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric static bool IsStdString(QualType T) { 500b57cec5SDimitry Andric if (const ElaboratedType *QT = T->getAs<ElaboratedType>()) 510b57cec5SDimitry Andric T = QT->getNamedType(); 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric const TypedefType *TT = T->getAs<TypedefType>(); 540b57cec5SDimitry Andric if (!TT) 550b57cec5SDimitry Andric return false; 560b57cec5SDimitry Andric 570b57cec5SDimitry Andric const TypedefNameDecl *TD = TT->getDecl(); 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric if (!TD->isInStdNamespace()) 600b57cec5SDimitry Andric return false; 610b57cec5SDimitry Andric 620b57cec5SDimitry Andric return TD->getName() == "string"; 630b57cec5SDimitry Andric } 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric static bool IsClangType(const RecordDecl *RD) { 660b57cec5SDimitry Andric return RD->getName() == "Type" && InNamespace(RD, "clang"); 670b57cec5SDimitry Andric } 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric static bool IsClangDecl(const RecordDecl *RD) { 700b57cec5SDimitry Andric return RD->getName() == "Decl" && InNamespace(RD, "clang"); 710b57cec5SDimitry Andric } 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric static bool IsClangStmt(const RecordDecl *RD) { 740b57cec5SDimitry Andric return RD->getName() == "Stmt" && InNamespace(RD, "clang"); 750b57cec5SDimitry Andric } 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric static bool IsClangAttr(const RecordDecl *RD) { 780b57cec5SDimitry Andric return RD->getName() == "Attr" && InNamespace(RD, "clang"); 790b57cec5SDimitry Andric } 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric static bool IsStdVector(QualType T) { 820b57cec5SDimitry Andric const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>(); 830b57cec5SDimitry Andric if (!TS) 840b57cec5SDimitry Andric return false; 850b57cec5SDimitry Andric 860b57cec5SDimitry Andric TemplateName TM = TS->getTemplateName(); 870b57cec5SDimitry Andric TemplateDecl *TD = TM.getAsTemplateDecl(); 880b57cec5SDimitry Andric 890b57cec5SDimitry Andric if (!TD || !InNamespace(TD, "std")) 900b57cec5SDimitry Andric return false; 910b57cec5SDimitry Andric 920b57cec5SDimitry Andric return TD->getName() == "vector"; 930b57cec5SDimitry Andric } 940b57cec5SDimitry Andric 950b57cec5SDimitry Andric static bool IsSmallVector(QualType T) { 960b57cec5SDimitry Andric const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>(); 970b57cec5SDimitry Andric if (!TS) 980b57cec5SDimitry Andric return false; 990b57cec5SDimitry Andric 1000b57cec5SDimitry Andric TemplateName TM = TS->getTemplateName(); 1010b57cec5SDimitry Andric TemplateDecl *TD = TM.getAsTemplateDecl(); 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric if (!TD || !InNamespace(TD, "llvm")) 1040b57cec5SDimitry Andric return false; 1050b57cec5SDimitry Andric 1060b57cec5SDimitry Andric return TD->getName() == "SmallVector"; 1070b57cec5SDimitry Andric } 1080b57cec5SDimitry Andric 1090b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1100b57cec5SDimitry Andric // CHECK: a StringRef should not be bound to a temporary std::string whose 1110b57cec5SDimitry Andric // lifetime is shorter than the StringRef's. 1120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric namespace { 1150b57cec5SDimitry Andric class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> { 1160b57cec5SDimitry Andric const Decl *DeclWithIssue; 1170b57cec5SDimitry Andric BugReporter &BR; 1180b57cec5SDimitry Andric const CheckerBase *Checker; 1190b57cec5SDimitry Andric 1200b57cec5SDimitry Andric public: 1210b57cec5SDimitry Andric StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br, 1220b57cec5SDimitry Andric const CheckerBase *checker) 1230b57cec5SDimitry Andric : DeclWithIssue(declWithIssue), BR(br), Checker(checker) {} 1240b57cec5SDimitry Andric void VisitChildren(Stmt *S) { 1250b57cec5SDimitry Andric for (Stmt *Child : S->children()) 1260b57cec5SDimitry Andric if (Child) 1270b57cec5SDimitry Andric Visit(Child); 1280b57cec5SDimitry Andric } 1290b57cec5SDimitry Andric void VisitStmt(Stmt *S) { VisitChildren(S); } 1300b57cec5SDimitry Andric void VisitDeclStmt(DeclStmt *DS); 1310b57cec5SDimitry Andric private: 1320b57cec5SDimitry Andric void VisitVarDecl(VarDecl *VD); 1330b57cec5SDimitry Andric }; 1340b57cec5SDimitry Andric } // end anonymous namespace 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR, 1370b57cec5SDimitry Andric const CheckerBase *Checker) { 1380b57cec5SDimitry Andric StringRefCheckerVisitor walker(D, BR, Checker); 1390b57cec5SDimitry Andric walker.Visit(D->getBody()); 1400b57cec5SDimitry Andric } 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { 1430b57cec5SDimitry Andric VisitChildren(S); 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric for (auto *I : S->decls()) 1460b57cec5SDimitry Andric if (VarDecl *VD = dyn_cast<VarDecl>(I)) 1470b57cec5SDimitry Andric VisitVarDecl(VD); 1480b57cec5SDimitry Andric } 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { 1510b57cec5SDimitry Andric Expr *Init = VD->getInit(); 1520b57cec5SDimitry Andric if (!Init) 1530b57cec5SDimitry Andric return; 1540b57cec5SDimitry Andric 1550b57cec5SDimitry Andric // Pattern match for: 1560b57cec5SDimitry Andric // StringRef x = call() (where call returns std::string) 1570b57cec5SDimitry Andric if (!IsLLVMStringRef(VD->getType())) 1580b57cec5SDimitry Andric return; 1590b57cec5SDimitry Andric ExprWithCleanups *Ex1 = dyn_cast<ExprWithCleanups>(Init); 1600b57cec5SDimitry Andric if (!Ex1) 1610b57cec5SDimitry Andric return; 1620b57cec5SDimitry Andric CXXConstructExpr *Ex2 = dyn_cast<CXXConstructExpr>(Ex1->getSubExpr()); 1630b57cec5SDimitry Andric if (!Ex2 || Ex2->getNumArgs() != 1) 1640b57cec5SDimitry Andric return; 1650b57cec5SDimitry Andric ImplicitCastExpr *Ex3 = dyn_cast<ImplicitCastExpr>(Ex2->getArg(0)); 1660b57cec5SDimitry Andric if (!Ex3) 1670b57cec5SDimitry Andric return; 1680b57cec5SDimitry Andric CXXConstructExpr *Ex4 = dyn_cast<CXXConstructExpr>(Ex3->getSubExpr()); 1690b57cec5SDimitry Andric if (!Ex4 || Ex4->getNumArgs() != 1) 1700b57cec5SDimitry Andric return; 1710b57cec5SDimitry Andric ImplicitCastExpr *Ex5 = dyn_cast<ImplicitCastExpr>(Ex4->getArg(0)); 1720b57cec5SDimitry Andric if (!Ex5) 1730b57cec5SDimitry Andric return; 1740b57cec5SDimitry Andric CXXBindTemporaryExpr *Ex6 = dyn_cast<CXXBindTemporaryExpr>(Ex5->getSubExpr()); 1750b57cec5SDimitry Andric if (!Ex6 || !IsStdString(Ex6->getType())) 1760b57cec5SDimitry Andric return; 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric // Okay, badness! Report an error. 1790b57cec5SDimitry Andric const char *desc = "StringRef should not be bound to temporary " 1800b57cec5SDimitry Andric "std::string that it outlives"; 1810b57cec5SDimitry Andric PathDiagnosticLocation VDLoc = 1820b57cec5SDimitry Andric PathDiagnosticLocation::createBegin(VD, BR.getSourceManager()); 1830b57cec5SDimitry Andric BR.EmitBasicReport(DeclWithIssue, Checker, desc, "LLVM Conventions", desc, 1840b57cec5SDimitry Andric VDLoc, Init->getSourceRange()); 1850b57cec5SDimitry Andric } 1860b57cec5SDimitry Andric 1870b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1880b57cec5SDimitry Andric // CHECK: Clang AST nodes should not have fields that can allocate 1890b57cec5SDimitry Andric // memory. 1900b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1910b57cec5SDimitry Andric 1920b57cec5SDimitry Andric static bool AllocatesMemory(QualType T) { 1930b57cec5SDimitry Andric return IsStdVector(T) || IsStdString(T) || IsSmallVector(T); 1940b57cec5SDimitry Andric } 1950b57cec5SDimitry Andric 1960b57cec5SDimitry Andric // This type checking could be sped up via dynamic programming. 1970b57cec5SDimitry Andric static bool IsPartOfAST(const CXXRecordDecl *R) { 1980b57cec5SDimitry Andric if (IsClangStmt(R) || IsClangType(R) || IsClangDecl(R) || IsClangAttr(R)) 1990b57cec5SDimitry Andric return true; 2000b57cec5SDimitry Andric 2010b57cec5SDimitry Andric for (const auto &BS : R->bases()) { 2020b57cec5SDimitry Andric QualType T = BS.getType(); 2030b57cec5SDimitry Andric if (const RecordType *baseT = T->getAs<RecordType>()) { 2040b57cec5SDimitry Andric CXXRecordDecl *baseD = cast<CXXRecordDecl>(baseT->getDecl()); 2050b57cec5SDimitry Andric if (IsPartOfAST(baseD)) 2060b57cec5SDimitry Andric return true; 2070b57cec5SDimitry Andric } 2080b57cec5SDimitry Andric } 2090b57cec5SDimitry Andric 2100b57cec5SDimitry Andric return false; 2110b57cec5SDimitry Andric } 2120b57cec5SDimitry Andric 2130b57cec5SDimitry Andric namespace { 2140b57cec5SDimitry Andric class ASTFieldVisitor { 2150b57cec5SDimitry Andric SmallVector<FieldDecl*, 10> FieldChain; 2160b57cec5SDimitry Andric const CXXRecordDecl *Root; 2170b57cec5SDimitry Andric BugReporter &BR; 2180b57cec5SDimitry Andric const CheckerBase *Checker; 2190b57cec5SDimitry Andric 2200b57cec5SDimitry Andric public: 2210b57cec5SDimitry Andric ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br, 2220b57cec5SDimitry Andric const CheckerBase *checker) 2230b57cec5SDimitry Andric : Root(root), BR(br), Checker(checker) {} 2240b57cec5SDimitry Andric 2250b57cec5SDimitry Andric void Visit(FieldDecl *D); 2260b57cec5SDimitry Andric void ReportError(QualType T); 2270b57cec5SDimitry Andric }; 2280b57cec5SDimitry Andric } // end anonymous namespace 2290b57cec5SDimitry Andric 2300b57cec5SDimitry Andric static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR, 2310b57cec5SDimitry Andric const CheckerBase *Checker) { 2320b57cec5SDimitry Andric if (!IsPartOfAST(R)) 2330b57cec5SDimitry Andric return; 2340b57cec5SDimitry Andric 2350b57cec5SDimitry Andric for (auto *I : R->fields()) { 2360b57cec5SDimitry Andric ASTFieldVisitor walker(R, BR, Checker); 2370b57cec5SDimitry Andric walker.Visit(I); 2380b57cec5SDimitry Andric } 2390b57cec5SDimitry Andric } 2400b57cec5SDimitry Andric 2410b57cec5SDimitry Andric void ASTFieldVisitor::Visit(FieldDecl *D) { 2420b57cec5SDimitry Andric FieldChain.push_back(D); 2430b57cec5SDimitry Andric 2440b57cec5SDimitry Andric QualType T = D->getType(); 2450b57cec5SDimitry Andric 2460b57cec5SDimitry Andric if (AllocatesMemory(T)) 2470b57cec5SDimitry Andric ReportError(T); 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric if (const RecordType *RT = T->getAs<RecordType>()) { 2500b57cec5SDimitry Andric const RecordDecl *RD = RT->getDecl()->getDefinition(); 2510b57cec5SDimitry Andric for (auto *I : RD->fields()) 2520b57cec5SDimitry Andric Visit(I); 2530b57cec5SDimitry Andric } 2540b57cec5SDimitry Andric 2550b57cec5SDimitry Andric FieldChain.pop_back(); 2560b57cec5SDimitry Andric } 2570b57cec5SDimitry Andric 2580b57cec5SDimitry Andric void ASTFieldVisitor::ReportError(QualType T) { 2590b57cec5SDimitry Andric SmallString<1024> buf; 2600b57cec5SDimitry Andric llvm::raw_svector_ostream os(buf); 2610b57cec5SDimitry Andric 2620b57cec5SDimitry Andric os << "AST class '" << Root->getName() << "' has a field '" 2630b57cec5SDimitry Andric << FieldChain.front()->getName() << "' that allocates heap memory"; 2640b57cec5SDimitry Andric if (FieldChain.size() > 1) { 2650b57cec5SDimitry Andric os << " via the following chain: "; 2660b57cec5SDimitry Andric bool isFirst = true; 2670b57cec5SDimitry Andric for (SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(), 2680b57cec5SDimitry Andric E=FieldChain.end(); I!=E; ++I) { 2690b57cec5SDimitry Andric if (!isFirst) 2700b57cec5SDimitry Andric os << '.'; 2710b57cec5SDimitry Andric else 2720b57cec5SDimitry Andric isFirst = false; 2730b57cec5SDimitry Andric os << (*I)->getName(); 2740b57cec5SDimitry Andric } 2750b57cec5SDimitry Andric } 276*81ad6265SDimitry Andric os << " (type " << FieldChain.back()->getType() << ")"; 2770b57cec5SDimitry Andric 2780b57cec5SDimitry Andric // Note that this will fire for every translation unit that uses this 2790b57cec5SDimitry Andric // class. This is suboptimal, but at least scan-build will merge 2800b57cec5SDimitry Andric // duplicate HTML reports. In the future we need a unified way of merging 2810b57cec5SDimitry Andric // duplicate reports across translation units. For C++ classes we cannot 2820b57cec5SDimitry Andric // just report warnings when we see an out-of-line method definition for a 2830b57cec5SDimitry Andric // class, as that heuristic doesn't always work (the complete definition of 2840b57cec5SDimitry Andric // the class may be in the header file, for example). 2850b57cec5SDimitry Andric PathDiagnosticLocation L = PathDiagnosticLocation::createBegin( 2860b57cec5SDimitry Andric FieldChain.front(), BR.getSourceManager()); 2870b57cec5SDimitry Andric BR.EmitBasicReport(Root, Checker, "AST node allocates heap memory", 2880b57cec5SDimitry Andric "LLVM Conventions", os.str(), L); 2890b57cec5SDimitry Andric } 2900b57cec5SDimitry Andric 2910b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 2920b57cec5SDimitry Andric // LLVMConventionsChecker 2930b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 2940b57cec5SDimitry Andric 2950b57cec5SDimitry Andric namespace { 2960b57cec5SDimitry Andric class LLVMConventionsChecker : public Checker< 2970b57cec5SDimitry Andric check::ASTDecl<CXXRecordDecl>, 2980b57cec5SDimitry Andric check::ASTCodeBody > { 2990b57cec5SDimitry Andric public: 3000b57cec5SDimitry Andric void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr, 3010b57cec5SDimitry Andric BugReporter &BR) const { 3020b57cec5SDimitry Andric if (R->isCompleteDefinition()) 3030b57cec5SDimitry Andric CheckASTMemory(R, BR, this); 3040b57cec5SDimitry Andric } 3050b57cec5SDimitry Andric 3060b57cec5SDimitry Andric void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, 3070b57cec5SDimitry Andric BugReporter &BR) const { 3080b57cec5SDimitry Andric CheckStringRefAssignedTemporary(D, BR, this); 3090b57cec5SDimitry Andric } 3100b57cec5SDimitry Andric }; 3110b57cec5SDimitry Andric } 3120b57cec5SDimitry Andric 3130b57cec5SDimitry Andric void ento::registerLLVMConventionsChecker(CheckerManager &mgr) { 3140b57cec5SDimitry Andric mgr.registerChecker<LLVMConventionsChecker>(); 3150b57cec5SDimitry Andric } 3160b57cec5SDimitry Andric 3175ffd83dbSDimitry Andric bool ento::shouldRegisterLLVMConventionsChecker(const CheckerManager &mgr) { 3180b57cec5SDimitry Andric return true; 3190b57cec5SDimitry Andric } 320