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