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/Core/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 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 TypedefDecl *TD = TT->getDecl(); 59 60 if (!InNamespace(TD, "std")) 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 llvm::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 BugReporter &BR; 118 public: 119 StringRefCheckerVisitor(BugReporter &br) : BR(br) {} 120 void VisitChildren(Stmt *S) { 121 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ; 122 I != E; ++I) 123 if (Stmt *child = *I) 124 Visit(child); 125 } 126 void VisitStmt(Stmt *S) { VisitChildren(S); } 127 void VisitDeclStmt(DeclStmt *DS); 128 private: 129 void VisitVarDecl(VarDecl *VD); 130 }; 131 } // end anonymous namespace 132 133 static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) { 134 StringRefCheckerVisitor walker(BR); 135 walker.Visit(D->getBody()); 136 } 137 138 void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { 139 VisitChildren(S); 140 141 for (DeclStmt::decl_iterator I = S->decl_begin(), E = S->decl_end();I!=E; ++I) 142 if (VarDecl *VD = dyn_cast<VarDecl>(*I)) 143 VisitVarDecl(VD); 144 } 145 146 void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { 147 Expr *Init = VD->getInit(); 148 if (!Init) 149 return; 150 151 // Pattern match for: 152 // llvm::StringRef x = call() (where call returns std::string) 153 if (!IsLLVMStringRef(VD->getType())) 154 return; 155 ExprWithCleanups *Ex1 = dyn_cast<ExprWithCleanups>(Init); 156 if (!Ex1) 157 return; 158 CXXConstructExpr *Ex2 = dyn_cast<CXXConstructExpr>(Ex1->getSubExpr()); 159 if (!Ex2 || Ex2->getNumArgs() != 1) 160 return; 161 ImplicitCastExpr *Ex3 = dyn_cast<ImplicitCastExpr>(Ex2->getArg(0)); 162 if (!Ex3) 163 return; 164 CXXConstructExpr *Ex4 = dyn_cast<CXXConstructExpr>(Ex3->getSubExpr()); 165 if (!Ex4 || Ex4->getNumArgs() != 1) 166 return; 167 ImplicitCastExpr *Ex5 = dyn_cast<ImplicitCastExpr>(Ex4->getArg(0)); 168 if (!Ex5) 169 return; 170 CXXBindTemporaryExpr *Ex6 = dyn_cast<CXXBindTemporaryExpr>(Ex5->getSubExpr()); 171 if (!Ex6 || !IsStdString(Ex6->getType())) 172 return; 173 174 // Okay, badness! Report an error. 175 const char *desc = "StringRef should not be bound to temporary " 176 "std::string that it outlives"; 177 178 BR.EmitBasicReport(desc, "LLVM Conventions", desc, 179 VD->getLocStart(), Init->getSourceRange()); 180 } 181 182 //===----------------------------------------------------------------------===// 183 // CHECK: Clang AST nodes should not have fields that can allocate 184 // memory. 185 //===----------------------------------------------------------------------===// 186 187 static bool AllocatesMemory(QualType T) { 188 return IsStdVector(T) || IsStdString(T) || IsSmallVector(T); 189 } 190 191 // This type checking could be sped up via dynamic programming. 192 static bool IsPartOfAST(const CXXRecordDecl *R) { 193 if (IsClangStmt(R) || IsClangType(R) || IsClangDecl(R) || IsClangAttr(R)) 194 return true; 195 196 for (CXXRecordDecl::base_class_const_iterator I = R->bases_begin(), 197 E = R->bases_end(); I!=E; ++I) { 198 CXXBaseSpecifier BS = *I; 199 QualType T = BS.getType(); 200 if (const RecordType *baseT = T->getAs<RecordType>()) { 201 CXXRecordDecl *baseD = cast<CXXRecordDecl>(baseT->getDecl()); 202 if (IsPartOfAST(baseD)) 203 return true; 204 } 205 } 206 207 return false; 208 } 209 210 namespace { 211 class ASTFieldVisitor { 212 llvm::SmallVector<FieldDecl*, 10> FieldChain; 213 CXXRecordDecl *Root; 214 BugReporter &BR; 215 public: 216 ASTFieldVisitor(CXXRecordDecl *root, BugReporter &br) 217 : Root(root), BR(br) {} 218 219 void Visit(FieldDecl *D); 220 void ReportError(QualType T); 221 }; 222 } // end anonymous namespace 223 224 static void CheckASTMemory(CXXRecordDecl *R, BugReporter &BR) { 225 if (!IsPartOfAST(R)) 226 return; 227 228 for (RecordDecl::field_iterator I = R->field_begin(), E = R->field_end(); 229 I != E; ++I) { 230 ASTFieldVisitor walker(R, BR); 231 walker.Visit(*I); 232 } 233 } 234 235 void ASTFieldVisitor::Visit(FieldDecl *D) { 236 FieldChain.push_back(D); 237 238 QualType T = D->getType(); 239 240 if (AllocatesMemory(T)) 241 ReportError(T); 242 243 if (const RecordType *RT = T->getAs<RecordType>()) { 244 const RecordDecl *RD = RT->getDecl()->getDefinition(); 245 for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); 246 I != E; ++I) 247 Visit(*I); 248 } 249 250 FieldChain.pop_back(); 251 } 252 253 void ASTFieldVisitor::ReportError(QualType T) { 254 llvm::SmallString<1024> buf; 255 llvm::raw_svector_ostream os(buf); 256 257 os << "AST class '" << Root->getName() << "' has a field '" 258 << FieldChain.front()->getName() << "' that allocates heap memory"; 259 if (FieldChain.size() > 1) { 260 os << " via the following chain: "; 261 bool isFirst = true; 262 for (llvm::SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(), 263 E=FieldChain.end(); I!=E; ++I) { 264 if (!isFirst) 265 os << '.'; 266 else 267 isFirst = false; 268 os << (*I)->getName(); 269 } 270 } 271 os << " (type " << FieldChain.back()->getType().getAsString() << ")"; 272 os.flush(); 273 274 // Note that this will fire for every translation unit that uses this 275 // class. This is suboptimal, but at least scan-build will merge 276 // duplicate HTML reports. In the future we need a unified way of merging 277 // duplicate reports across translation units. For C++ classes we cannot 278 // just report warnings when we see an out-of-line method definition for a 279 // class, as that heuristic doesn't always work (the complete definition of 280 // the class may be in the header file, for example). 281 BR.EmitBasicReport("AST node allocates heap memory", "LLVM Conventions", 282 os.str(), FieldChain.front()->getLocStart()); 283 } 284 285 //===----------------------------------------------------------------------===// 286 // Entry point for all checks. 287 //===----------------------------------------------------------------------===// 288 289 static void ScanCodeDecls(DeclContext *DC, BugReporter &BR) { 290 for (DeclContext::decl_iterator I=DC->decls_begin(), E=DC->decls_end(); 291 I!=E ; ++I) { 292 293 Decl *D = *I; 294 295 if (D->hasBody()) 296 CheckStringRefAssignedTemporary(D, BR); 297 298 if (CXXRecordDecl *R = dyn_cast<CXXRecordDecl>(D)) 299 if (R->isDefinition()) 300 CheckASTMemory(R, BR); 301 302 if (DeclContext *DC_child = dyn_cast<DeclContext>(D)) 303 ScanCodeDecls(DC_child, BR); 304 } 305 } 306 307 void ento::CheckLLVMConventions(TranslationUnitDecl &TU, 308 BugReporter &BR) { 309 ScanCodeDecls(&TU, BR); 310 } 311 312