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