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