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